diff --git a/openbis/dist/server/jetty.xml b/openbis/dist/server/jetty.xml index 21f54a0b1815e67470fa04c50164450ace1df47a..e781e6adce575cd0f9869bb2cead98c230a32f8b 100644 --- a/openbis/dist/server/jetty.xml +++ b/openbis/dist/server/jetty.xml @@ -56,6 +56,14 @@ </New> </Arg> </Call> + <Call name="addAppProvider"> + <Arg> + <New class="org.eclipse.jetty.deploy.providers.ContextProvider"> + <Set name="monitoredDir"><Property name="jetty.home" default="." />/contexts</Set> + <Set name="scanInterval">1</Set> + </New> + </Arg> + </Call> </New> </Arg> </Call> diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/coreplugin/CorePluginsInjectingPropertyPlaceholderConfigurer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/coreplugin/CorePluginsInjectingPropertyPlaceholderConfigurer.java index b9f04ef2e8b26df6988c55f5b051f63bd257fe08..94b47e491dec9ea56ef6112e070ae07f501ae28b 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/coreplugin/CorePluginsInjectingPropertyPlaceholderConfigurer.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/coreplugin/CorePluginsInjectingPropertyPlaceholderConfigurer.java @@ -22,6 +22,7 @@ import java.util.Properties; import ch.systemsx.cisd.common.maintenance.MaintenanceTaskUtils; import ch.systemsx.cisd.common.spring.ExposablePropertyPlaceholderConfigurer; import ch.systemsx.cisd.openbis.generic.server.dataaccess.DataStoreServerBasedDataSourceProvider; +import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CustomImport; import ch.systemsx.cisd.openbis.generic.shared.coreplugin.CorePluginScanner.ScannerType; import ch.systemsx.cisd.openbis.generic.shared.coreplugin.CorePluginsInjector; @@ -50,11 +51,14 @@ public class CorePluginsInjectingPropertyPlaceholderConfigurer extends CustomImport.PropertyNames.CUSTOM_IMPORTS.getName()); PluginType queryDatabases = new PluginType("query-databases", "query-databases"); PluginType miscellaneous = new PluginType("miscellaneous", null); - PluginType webapps = new PluginType("webapps", "webapps"); + PluginType webapps = new PluginType("webapps", BasicConstant.WEB_APPS_PROPERTY); new CorePluginsInjector(ScannerType.AS, new IPluginType[] { maintenanceTasks, customImports, queryDatabases, miscellaneous, dssDataSources, webapps }).injectCorePlugins(properties); + + // Inject the web apps into jetty + new JettyWebAppPluginInjector(properties).injectWebApps(); } private PluginType createPluginTypeDssDataSources() diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/coreplugin/JettyWebAppPluginInjector.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/coreplugin/JettyWebAppPluginInjector.java new file mode 100644 index 0000000000000000000000000000000000000000..f3f19202d6c178c77ee32433e7833094f00908d4 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/coreplugin/JettyWebAppPluginInjector.java @@ -0,0 +1,207 @@ +/* + * Copyright 2012 ETH Zuerich, CISD + * + * 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.generic.server.coreplugin; + +import java.io.File; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.filesystem.FileUtilities; +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.common.utilities.PropertyParametersUtil; +import ch.systemsx.cisd.common.utilities.PropertyUtils; +import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant; + +/** + * A class that injects web apps into jetty. + * + * @author Chandrasekhar Ramakrishnan + */ +public class JettyWebAppPluginInjector +{ + /** + * A utility class that generates a configuration file for a Jetty context for a webapp. + * + * @author Chandrasekhar Ramakrishnan + */ + public static class ContextConfiguration + { + private final String webapp; + + private final Properties properties; + + public ContextConfiguration(String webapp, Properties properties) + { + this.webapp = webapp; + this.properties = properties; + } + + public String getConfigurationOrNull() + { + + String resourceBase = + properties.getProperty(JettyWebAppPluginInjector.WEB_APP_FOLDER_PROPERTY); + if (null == resourceBase) + { + JettyWebAppPluginInjector.operationLog.error("No configuration property for " + + JettyWebAppPluginInjector.WEB_APP_FOLDER_PROPERTY + + " was found in webapp properties :\n" + properties.toString()); + return null; + } + String configuration = + "<Configure class=\"org.eclipse.jetty.server.handler.ContextHandler\">\n" + + " <Call class=\"org.eclipse.jetty.util.log.Log\" name=\"debug\"><Arg>Configure [" + + webapp + + "] webapp</Arg></Call>\n" + + " <Set name=\"contextPath\">/" + + webapp + + "</Set>\n" + + " <Set name=\"resourceBase\">" + + resourceBase + + "</Set>\n" + + " <Set name=\"handler\">\n" + + " <New class=\"org.eclipse.jetty.server.handler.ResourceHandler\">\n" + + " <Set name=\"welcomeFiles\">\n" + + " <Array type=\"String\">\n" + + " <Item>index.html</Item>\n" + " </Array>\n" + + " </Set>\n" + + " <Set name=\"cacheControl\">max-age=3600,public</Set>\n" + + " </New>\n" + " </Set>\n" + "</Configure>"; + + return configuration; + } + } + + // This is the folder referenced in jetty.xml. It must have the same name as in the + // jetty.xml configuration file. + private static final String CONTEXT_FOLDER = "contexts"; + + private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, + JettyWebAppPluginInjector.class); + + private final List<String> webapps; + + private final Map<String, Properties> webappProperties; + + // This is initialized in ensureContextFolderExists and is invalid before that + // method runs + private File contextsFolder = null; + + public static final String WEB_APP_FOLDER_PROPERTY = "webapp-folder"; + + private static Map<String, Properties> extractWebappProperties(Properties props, + List<String> webapps) + { + HashMap<String, Properties> map = new HashMap<String, Properties>(); + + for (String webapp : webapps) + { + Properties webappProperties = + PropertyParametersUtil.extractSingleSectionProperties(props, webapp, false) + .getProperties(); + map.put(webapp, webappProperties); + } + return map; + } + + public JettyWebAppPluginInjector(Properties props) + { + List<String> appList = + PropertyUtils.tryGetListInOriginalCase(props, BasicConstant.WEB_APPS_PROPERTY); + webapps = (null == appList) ? Collections.<String> emptyList() : appList; + webappProperties = extractWebappProperties(props, webapps); + } + + public void injectWebApps() + { + logWebappsToInject(); + + // Leave if there is nothing to do + if (webapps.size() < 1) + { + return; + } + if (false == isRunningUnderJetty()) + { + // We are not running in Jetty. Log and then get out. + operationLog.error("Not running under jetty. Cannot inject webapps."); + return; + } + if (false == ensureContextFolderExists()) + { + operationLog.error("Could not create folder " + contextsFolder.getAbsolutePath() + + ". Cannot inject webapps."); + return; + } + for (String webapp : webapps) + { + injectWebapp(webapp, webappProperties.get(webapp)); + } + } + + private void logWebappsToInject() + { + StringBuilder sb = new StringBuilder(); + sb.append("Found " + webapps.size()); + if (1 == webapps.size()) + { + sb.append(" webapp"); + } else + { + sb.append(" webapps"); + } + sb.append(" to inject."); + + operationLog.info(sb.toString()); + } + + private boolean ensureContextFolderExists() + { + // This must be non-null because isRunningUnderJetty is true + String jettyHomePath = System.getProperty("jetty.home"); + contextsFolder = new File(jettyHomePath, CONTEXT_FOLDER); + if (false == contextsFolder.exists()) + { + return contextsFolder.mkdir(); + } + return true; + } + + private boolean isRunningUnderJetty() + { + return null != System.getProperty("jetty.home"); + } + + private void injectWebapp(String webapp, Properties props) + { + String webappDisplayName = webapp; + operationLog.info("Injecting webapp [" + webappDisplayName + "]"); + ContextConfiguration config = new ContextConfiguration(webappDisplayName, props); + File contextConfig = new File(contextsFolder, webappDisplayName + ".xml"); + String configContent = config.getConfigurationOrNull(); + if (null != configContent) + { + FileUtilities.writeToFile(contextConfig, configContent); + } + } +} diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/BasicConstant.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/BasicConstant.java index 99a2e7ab106683e6fa26993492076493da35836c..831d53ff2f867e4b174aaf374358c9cd300bb3ff 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/BasicConstant.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/BasicConstant.java @@ -114,6 +114,8 @@ public class BasicConstant public static final String DATA_SET_UPLOAD_CLIENT_PATH = "data-set-uploader-launch.jnlp"; + public static final String WEB_APPS_PROPERTY = "webapps"; + private BasicConstant() { } diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/coreplugin/CorePluginsInjectorTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/coreplugin/CorePluginsInjectorTest.java index a28c150a85dca4bdf0e7a86740a3f04c2570283e..71f5555afd51d4cb07a0f14047cfd5ad7e4bf252 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/coreplugin/CorePluginsInjectorTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/coreplugin/CorePluginsInjectorTest.java @@ -44,6 +44,10 @@ import ch.systemsx.cisd.common.logging.ISimpleLogger; import ch.systemsx.cisd.common.logging.LogLevel; import ch.systemsx.cisd.common.test.RecordingMatcher; import ch.systemsx.cisd.common.utilities.ExtendedProperties; +import ch.systemsx.cisd.common.utilities.PropertyParametersUtil; +import ch.systemsx.cisd.common.utilities.PropertyUtils; +import ch.systemsx.cisd.openbis.generic.server.coreplugin.JettyWebAppPluginInjector; +import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant; import ch.systemsx.cisd.openbis.generic.shared.coreplugin.CorePluginScanner.ScannerType; /** @@ -354,10 +358,36 @@ public class CorePluginsInjectorTest extends AbstractFileSystemTestCase injector.injectCorePlugins(properties); - assertEquals("example-webapp", properties.getProperty("webapps")); - String webappFolder = properties.getProperty("example-webapp.webapp-folder"); + List<String> appList = + PropertyUtils.tryGetListInOriginalCase(properties, BasicConstant.WEB_APPS_PROPERTY); + assertEquals(1, appList.size()); + assertEquals("example-webapp", appList.get(0)); + Properties exampleWebappProperties = + PropertyParametersUtil.extractSingleSectionProperties(properties, appList.get(0), + false).getProperties(); + String webappFolder = + exampleWebappProperties + .getProperty(JettyWebAppPluginInjector.WEB_APP_FOLDER_PROPERTY); assertEquals(webapps.toString() + "/example-webapp/html", webappFolder); + JettyWebAppPluginInjector.ContextConfiguration configuration = + new JettyWebAppPluginInjector.ContextConfiguration("example-webapp", + exampleWebappProperties); + String expectedConfiguration = + "<Configure class=\"org.eclipse.jetty.server.handler.ContextHandler\">\n" + + " <Call class=\"org.eclipse.jetty.util.log.Log\" name=\"debug\"><Arg>Configure [example-webapp] webapp</Arg></Call>\n" + + " <Set name=\"contextPath\">/example-webapp</Set>\n" + + " <Set name=\"resourceBase\">targets/unit-test-wd/ch.systemsx.cisd.openbis.generic.shared.coreplugin.CorePluginsInjectorTest/core-plugins/screening/1/dss/webapps/example-webapp/html</Set>\n" + + " <Set name=\"handler\">\n" + + " <New class=\"org.eclipse.jetty.server.handler.ResourceHandler\">\n" + + " <Set name=\"welcomeFiles\">\n" + + " <Array type=\"String\">\n" + + " <Item>index.html</Item>\n" + " </Array>\n" + + " </Set>\n" + + " <Set name=\"cacheControl\">max-age=3600,public</Set>\n" + + " </New>\n" + " </Set>\n" + "</Configure>"; + assertEquals(expectedConfiguration, configuration.getConfigurationOrNull()); + context.assertIsSatisfied(); }