From 1eadf135a809f3e30ff7c5f95833f9de94d9aae2 Mon Sep 17 00:00:00 2001 From: felmer <felmer> Date: Wed, 29 Jun 2016 11:56:19 +0000 Subject: [PATCH] SSDM-3745: JythonEvaluatorSpringComponent refactored. Subclass DataStoreServerJythonEvaluatorSpringComponent introduced which gets the Jython27EvaluatorFactory instance from Jython27FactoriesProvider by using Jython27ClassLoader for loading. Also in JythonTopLevelDataSetHandlerV2 Jython27InterpreterFactory is provided by Jython27FactoriesProvider. jython27 JAR not added to classpath in datastore_server.sh SVN: 36768 --- .../JythonEvaluatorSpringComponent.java | 14 ++- datastore_server/dist/datastore_server.sh | 5 +- .../v2/JythonTopLevelDataSetHandlerV2.java | 4 +- ...eServerJythonEvaluatorSpringComponent.java | 53 +++++++++ .../shared/utils/Jython27ClassLoader.java | 102 ++++++++++++++++++ .../utils/Jython27FactoriesProvider.java | 86 +++++++++++++++ .../source/java/dssApplicationContext.xml | 3 +- 7 files changed, 261 insertions(+), 6 deletions(-) create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServerJythonEvaluatorSpringComponent.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Jython27ClassLoader.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Jython27FactoriesProvider.java 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 85a3dbabfca..d0b27e54e80 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 048d10bb636..13e4aca8805 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 a5235021187..9d5bef5ba6e 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 00000000000..45794541cbd --- /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 00000000000..5fd272dbdea --- /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 00000000000..aa2b6f0a825 --- /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 c95e13700d9..f88e1176d82 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 -- GitLab