diff --git a/common/source/java/ch/systemsx/cisd/common/jython/evaluator/JythonEvaluatorSpringComponent.java b/common/source/java/ch/systemsx/cisd/common/jython/evaluator/JythonEvaluatorSpringComponent.java index 85a3dbabfca2d91461f73a11396f530b0c91607f..d0b27e54e805292069d772f894236056633eb4fb 100644 --- a/common/source/java/ch/systemsx/cisd/common/jython/evaluator/JythonEvaluatorSpringComponent.java +++ b/common/source/java/ch/systemsx/cisd/common/jython/evaluator/JythonEvaluatorSpringComponent.java @@ -41,10 +41,10 @@ public class JythonEvaluatorSpringComponent String jythonVersion = propertyConfigurer.getResolvedProps().getProperty("jython-version"); if ("2.7".equals(jythonVersion)) { - Evaluator.setFactory(new Jython27EvaluatorFactory()); + Evaluator.setFactory(createJython27EvaluatorFactory()); } else if ("2.5".equals(jythonVersion)) { - Evaluator.setFactory(new Jython25EvaluatorFactory()); + Evaluator.setFactory(createJython25EvaluatorFactory()); } else { String msg = @@ -56,4 +56,14 @@ public class JythonEvaluatorSpringComponent throw new BeanInitializationException(msg); } } + + protected IJythonEvaluatorFactory createJython25EvaluatorFactory() + { + return new Jython25EvaluatorFactory(); + } + + protected IJythonEvaluatorFactory createJython27EvaluatorFactory() + { + return new Jython27EvaluatorFactory(); + } } diff --git a/datastore_server/dist/datastore_server.sh b/datastore_server/dist/datastore_server.sh index 048d10bb636c97f5b5de426d6e3060eab728d18b..13e4aca880593442eecc83330261236b39b8872f 100755 --- a/datastore_server/dist/datastore_server.sh +++ b/datastore_server/dist/datastore_server.sh @@ -156,7 +156,10 @@ unzip -q lib/hdf5-windows-*.jar -d $LIB_FOLDER native/* # Build classpath from $LIB_FOLDER and $EXT_LIB_FOLDER content. # datastore_server.jar and common.jar have to appear before cifex.jar -CP=`echo $LIB_FOLDER/slf4j-log4j12-1.6.2.jar $LIB_FOLDER/datastore_server.jar $LIB_FOLDER/common.jar $LIB_FOLDER/dbmigration*.jar $LIB_FOLDER/*.jar $EXT_LIB_FOLDER/*.jar | sed 's/ /:/g'` +CP=`echo $LIB_FOLDER/slf4j-log4j12-1.6.2.jar $LIB_FOLDER/datastore_server.jar $LIB_FOLDER/common.jar \ + $LIB_FOLDER/dbmigration*.jar $LIB_FOLDER/*.jar $EXT_LIB_FOLDER/*.jar \ + | sed 's/\(.*\) [^ ]*jython27[^ ]* \(.*\)/\1 \2/g' \ + | sed 's/ /:/g'` CMD="${JAVA_BIN} ${JAVA_OPTS} ${JAVA_MEM_OPTS} -Dnative.libpath=$LIB_FOLDER/native -classpath $CP ch.systemsx.cisd.openbis.dss.generic.DataStoreServer" diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/api/v2/JythonTopLevelDataSetHandlerV2.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/api/v2/JythonTopLevelDataSetHandlerV2.java index a5235021187a6fcb46c7baff75991cced076482f..9d5bef5ba6eb914a220c2e7159c8114a42250e97 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/api/v2/JythonTopLevelDataSetHandlerV2.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/api/v2/JythonTopLevelDataSetHandlerV2.java @@ -25,7 +25,6 @@ import ch.systemsx.cisd.common.jython.IJythonFunction; import ch.systemsx.cisd.common.jython.IJythonInterpreter; import ch.systemsx.cisd.common.jython.IJythonInterpreterFactory; import ch.systemsx.cisd.common.jython.v25.Jython25InterpreterFactory; -import ch.systemsx.cisd.common.jython.v27.Jython27InterpreterFactory; import ch.systemsx.cisd.common.properties.PropertyUtils; import ch.systemsx.cisd.etlserver.DssRegistrationLogger; import ch.systemsx.cisd.etlserver.ITopLevelDataSetRegistratorDelegate; @@ -37,6 +36,7 @@ import ch.systemsx.cisd.etlserver.registrator.v2.AbstractDataSetRegistrationDeta import ch.systemsx.cisd.etlserver.registrator.v2.AbstractProgrammableTopLevelDataSetHandler; import ch.systemsx.cisd.etlserver.registrator.v2.DataSetRegistrationService; import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation; +import ch.systemsx.cisd.openbis.dss.generic.shared.utils.Jython27FactoriesProvider; /** * A top-level data set handler that runs a python (jython) script to register data sets. @@ -78,7 +78,7 @@ public class JythonTopLevelDataSetHandlerV2<T extends DataSetInformation> extend jythonInterpreterFactory = new Jython25InterpreterFactory(); } else { - jythonInterpreterFactory = new Jython27InterpreterFactory(); + jythonInterpreterFactory = Jython27FactoriesProvider.getInterpreterFactory(); } DssRegistrationHealthMonitor.getInstance(globalState.getOpenBisService(), diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServerJythonEvaluatorSpringComponent.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServerJythonEvaluatorSpringComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..45794541cbdb81b9824e23a9bd8547ab93885621 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServerJythonEvaluatorSpringComponent.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016 ETH Zuerich, SIS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.generic.server; + +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.jython.evaluator.IJythonEvaluatorFactory; +import ch.systemsx.cisd.common.jython.evaluator.JythonEvaluatorSpringComponent; +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.common.spring.ExposablePropertyPlaceholderConfigurer; +import ch.systemsx.cisd.openbis.dss.generic.shared.utils.Jython27ClassLoader; +import ch.systemsx.cisd.openbis.dss.generic.shared.utils.Jython27FactoriesProvider; + + +/** + * Jython evaluator component for DSS. Jython27EvaulatorFactory is loaded by {@link Jython27ClassLoader}. + * + * @author Franz-Josef Elmer + */ +public class DataStoreServerJythonEvaluatorSpringComponent extends JythonEvaluatorSpringComponent +{ + private final static Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, + DataStoreServerJythonEvaluatorSpringComponent.class); + + public DataStoreServerJythonEvaluatorSpringComponent(ExposablePropertyPlaceholderConfigurer propertyConfigurer) + { + super(propertyConfigurer); + } + + @Override + protected IJythonEvaluatorFactory createJython27EvaluatorFactory() + { + IJythonEvaluatorFactory evaluatorFactory = Jython27FactoriesProvider.getEvaluatorFactory(); + operationLog.info("Class loader of " + evaluatorFactory + ": " + evaluatorFactory.getClass().getClassLoader()); + return evaluatorFactory; + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Jython27ClassLoader.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Jython27ClassLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..5fd272dbdea066b22218a5018fee61263ffd709f --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Jython27ClassLoader.java @@ -0,0 +1,102 @@ +/* + * Copyright 2016 ETH Zuerich, SIS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.generic.shared.utils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; + +import org.apache.commons.io.IOUtils; + +import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; +import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; + +/** + * Special class loader for classes based on jython 2.7. The jython JAR file has to be provided in the + * constructor. + * <ul> + * <li>This class loader always try first to find and load the class from the specified JAR file. + * <li>If it isn't found and if its fully-qualified class name does not match <tt>*.v27.*</tt> the class is + * loaded by the application class loader. + * <li>A class not found in the provided JAR file and fully-qualified class name does match <tt>*.v27.*</tt> + * are loaded with this class loader. + * </ul> + * + * @author Franz-Josef Elmer + */ +public class Jython27ClassLoader extends ClassLoader +{ + private final ClassLoader jythonJarClassLoader; + + public Jython27ClassLoader(File jythonJar) + { + if (jythonJar.exists() == false) + { + throw new EnvironmentFailureException("JAR file does not exist: " + jythonJar.getAbsolutePath()); + } + if (jythonJar.isDirectory()) + { + throw new EnvironmentFailureException("JAR file is a directory: " + jythonJar.getAbsolutePath()); + } + try + { + URL url = jythonJar.toURI().toURL(); + jythonJarClassLoader = new URLClassLoader(new URL[] { url }, null); + } catch (MalformedURLException ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); + } + } + + @Override + public Class<?> loadClass(String name) throws ClassNotFoundException + { + try + { + return jythonJarClassLoader.loadClass(name); + } catch (ClassNotFoundException ex) + { + if (name.contains(".v27.") == false) + { + return super.loadClass(name); + } + synchronized (getClassLoadingLock(name)) + { + InputStream stream = getResourceAsStream(name.replace('.', '/') + ".class"); + if (stream != null) + { + try + { + byte[] bytes = IOUtils.toByteArray(stream); + return defineClass(name, bytes, 0, bytes.length); + } catch (IOException ex2) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex2); + } finally + { + IOUtils.closeQuietly(stream); + } + } + return super.loadClass(name); + } + } + } + +} \ No newline at end of file diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Jython27FactoriesProvider.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Jython27FactoriesProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..aa2b6f0a825fedd089d0405dde43bbecf9186669 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Jython27FactoriesProvider.java @@ -0,0 +1,86 @@ +/* + * Copyright 2016 ETH Zuerich, SIS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.generic.shared.utils; + +import java.io.File; + +import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; +import ch.systemsx.cisd.common.jython.IJythonInterpreterFactory; +import ch.systemsx.cisd.common.jython.evaluator.IJythonEvaluatorFactory; +import ch.systemsx.cisd.common.jython.v27.Jython27EvaluatorFactory; +import ch.systemsx.cisd.common.jython.v27.Jython27InterpreterFactory; + +/** + * Provider of a single {@link Jython27EvaluatorFactory} instance and a single {@link Jython27InterpreterFactory} + * instance. Both lazily loaded by {@link Jython27ClassLoader}. + * + * @author Franz-Josef Elmer + */ +public class Jython27FactoriesProvider +{ + private static final ClassLoader JYTHON_CLASS_LOADER = createJythonClassLoader(); + private static IJythonEvaluatorFactory jythonEvaluatorFactory; + private static IJythonInterpreterFactory jythonInterpreterFactory; + + private static ClassLoader createJythonClassLoader() + { + File[] files = new File("lib").listFiles(); + if (files != null) + { + for (File file : files) + { + if (file.getName().startsWith("jython27")) + { + return new Jython27ClassLoader(file); + } + } + } + return Jython27ClassLoader.class.getClassLoader(); + } + + public synchronized static IJythonEvaluatorFactory getEvaluatorFactory() + { + if (jythonEvaluatorFactory == null) + { + try + { + jythonEvaluatorFactory = (IJythonEvaluatorFactory) JYTHON_CLASS_LOADER.loadClass( + "ch.systemsx.cisd.common.jython.v27.Jython27EvaluatorFactory").newInstance(); + } catch (Exception ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); + } + } + return jythonEvaluatorFactory; + } + + public synchronized static IJythonInterpreterFactory getInterpreterFactory() + { + if (jythonInterpreterFactory == null) + { + try + { + jythonInterpreterFactory = (IJythonInterpreterFactory) JYTHON_CLASS_LOADER.loadClass( + "ch.systemsx.cisd.common.jython.v27.Jython27InterpreterFactory").newInstance(); + } catch (Exception ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); + } + } + return jythonInterpreterFactory; + } +} diff --git a/datastore_server/source/java/dssApplicationContext.xml b/datastore_server/source/java/dssApplicationContext.xml index c95e13700d9a6f2c84aae9f9b0de1beb608730d5..f88e1176d822042416992baca301a2671b457443 100644 --- a/datastore_server/source/java/dssApplicationContext.xml +++ b/datastore_server/source/java/dssApplicationContext.xml @@ -284,7 +284,8 @@ --> <bean id="cifs-server" class="ch.systemsx.cisd.openbis.dss.generic.server.cifs.CifsServer" destroy-method="stop"/> - <bean id="jython-evaluator" class="ch.systemsx.cisd.common.jython.evaluator.JythonEvaluatorSpringComponent" > + + <bean id="jython-evaluator" class="ch.systemsx.cisd.openbis.dss.generic.server.DataStoreServerJythonEvaluatorSpringComponent" > <constructor-arg ref="propertyConfigurer" /> </bean> </beans> \ No newline at end of file