// // ======================================================================== // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.osgi.boot.internal.serverfactory; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.eclipse.jetty.deploy.AppLifeCycle; import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.bindings.StandardStarter; import org.eclipse.jetty.deploy.bindings.StandardStopper; import org.eclipse.jetty.osgi.boot.BundleContextProvider; import org.eclipse.jetty.osgi.boot.BundleWebAppProvider; import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator; import org.eclipse.jetty.osgi.boot.OSGiDeployer; import org.eclipse.jetty.osgi.boot.OSGiServerConstants; import org.eclipse.jetty.osgi.boot.OSGiUndeployer; import org.eclipse.jetty.osgi.boot.ServiceContextProvider; import org.eclipse.jetty.osgi.boot.ServiceWebAppProvider; import org.eclipse.jetty.osgi.boot.internal.webapp.LibExtClassLoaderHelper; import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory; import org.eclipse.jetty.osgi.boot.utils.FakeURLClassLoader; import org.eclipse.jetty.osgi.boot.utils.TldBundleDiscoverer; import org.eclipse.jetty.osgi.boot.utils.Util; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.xml.XmlConfiguration; /** * ServerInstanceWrapper * * Configures and starts a jetty Server instance. */ public class ServerInstanceWrapper { /** * The value of this property points to the parent director of the jetty.xml * configuration file currently executed. Everything is passed as a URL to * support the case where the bundle is zipped. */ public static final String PROPERTY_THIS_JETTY_XML_FOLDER_URL = "this.jetty.xml.parent.folder.url"; private static Collection<TldBundleDiscoverer> __containerTldBundleDiscoverers = new ArrayList<TldBundleDiscoverer>(); private static Logger LOG = Log.getLogger(ServerInstanceWrapper.class.getName()); private final String _managedServerName; /** * The managed jetty server */ private Server _server; private ContextHandlerCollection _ctxtCollection; /** * This is the class loader that should be the parent classloader of any * webapp classloader. It is in fact the _libExtClassLoader with a trick to * let the TldScanner find the jars where the tld files are. */ private ClassLoader _commonParentClassLoaderForWebapps; private DeploymentManager _deploymentManager; /* ------------------------------------------------------------ */ public static void addContainerTldBundleDiscoverer (TldBundleDiscoverer tldBundleDiscoverer) { __containerTldBundleDiscoverers.add(tldBundleDiscoverer); } /* ------------------------------------------------------------ */ public static Collection<TldBundleDiscoverer> getContainerTldBundleDiscoverers() { return __containerTldBundleDiscoverers; } /* ------------------------------------------------------------ */ public static Server configure(Server server, List<URL> jettyConfigurations, Dictionary props) throws Exception { if (jettyConfigurations == null || jettyConfigurations.isEmpty()) { return server; } Map<String, Object> id_map = new HashMap<String, Object>(); if (server != null) { //Put in a mapping for the id "Server" and the name of the server as the instance being configured id_map.put("Server", server); id_map.put((String)props.get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME), server); } Map<String, String> properties = new HashMap<String, String>(); if (props != null) { Enumeration<Object> en = props.keys(); while (en.hasMoreElements()) { Object key = en.nextElement(); Object value = props.get(key); String keyStr = String.valueOf(key); String valStr = String.valueOf(value); properties.put(keyStr, valStr); if (server != null) server.setAttribute(keyStr, valStr); } } for (URL jettyConfiguration : jettyConfigurations) { try(Resource r = Resource.newResource(jettyConfiguration)) { // Execute a Jetty configuration file if (!r.exists()) { LOG.warn("File does not exist "+r); throw new IllegalStateException("No such jetty server config file: "+r); } XmlConfiguration config = new XmlConfiguration(r.getURL()); config.getIdMap().putAll(id_map); config.getProperties().putAll(properties); // #334062 compute the URL of the folder that contains the // conf file and set it as a property so we can compute relative paths // from it. String urlPath = jettyConfiguration.toString(); int lastSlash = urlPath.lastIndexOf('/'); if (lastSlash > 4) { urlPath = urlPath.substring(0, lastSlash); config.getProperties().put(PROPERTY_THIS_JETTY_XML_FOLDER_URL, urlPath); } Object o = config.configure(); if (server == null) server = (Server)o; id_map = config.getIdMap(); } catch (Exception e) { LOG.warn("Configuration error in " + jettyConfiguration); throw e; } } return server; } /* ------------------------------------------------------------ */ public ServerInstanceWrapper(String managedServerName) { _managedServerName = managedServerName; } /* ------------------------------------------------------------ */ public String getManagedServerName() { return _managedServerName; } /* ------------------------------------------------------------ */ /** * The classloader that should be the parent classloader for each webapp * deployed on this server. * * @return the classloader */ public ClassLoader getParentClassLoaderForWebapps() { return _commonParentClassLoaderForWebapps; } /* ------------------------------------------------------------ */ /** * @return The deployment manager registered on this server. */ public DeploymentManager getDeploymentManager() { return _deploymentManager; } /* ------------------------------------------------------------ */ /** * @return The app provider registered on this server. */ public Server getServer() { return _server; } /* ------------------------------------------------------------ */ /** * @return The collection of context handlers */ public ContextHandlerCollection getContextHandlerCollection() { return _ctxtCollection; } /* ------------------------------------------------------------ */ public void start(Server server, Dictionary props) throws Exception { _server = server; ClassLoader contextCl = Thread.currentThread().getContextClassLoader(); try { // passing this bundle's classloader as the context classloader // makes sure there is access to all the jetty's bundles ClassLoader libExtClassLoader = null; String sharedURLs = (String) props.get(OSGiServerConstants.MANAGED_JETTY_SHARED_LIB_FOLDER_URLS); List<File> shared = sharedURLs != null ? extractFiles(sharedURLs) : null; libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader(shared, null,JettyBootstrapActivator.class.getClassLoader()); if (LOG.isDebugEnabled()) LOG.debug("LibExtClassLoader = "+libExtClassLoader); Thread.currentThread().setContextClassLoader(libExtClassLoader); String jettyConfigurationUrls = (String) props.get(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS); List<URL> jettyConfigurations = jettyConfigurationUrls != null ? Util.fileNamesAsURLs(jettyConfigurationUrls, Util.DEFAULT_DELIMS) : null; _server = configure(server, jettyConfigurations, props); init(); //if support for jsp is enabled, we need to convert locations of bundles that contain tlds into urls. //these are tlds that we want jasper to treat as if they are on the container's classpath. Web bundles //can use the Require-TldBundle MANIFEST header to name other tld-containing bundles that should be regarded //as on the webapp classpath. if (!__containerTldBundleDiscoverers.isEmpty()) { Set<URL> urls = new HashSet<URL>(); //discover bundles with tlds that need to be on the container's classpath as URLs for (TldBundleDiscoverer d:__containerTldBundleDiscoverers) { URL[] list = d.getUrlsForBundlesWithTlds(_deploymentManager, BundleFileLocatorHelperFactory.getFactory().getHelper()); if (list != null) { for (URL u:list) urls.add(u); } } _commonParentClassLoaderForWebapps = new FakeURLClassLoader(libExtClassLoader, urls.toArray(new URL[urls.size()])); } else _commonParentClassLoaderForWebapps = libExtClassLoader; if (LOG.isDebugEnabled()) LOG.debug("common classloader = "+_commonParentClassLoaderForWebapps); server.start(); } catch (Exception e) { if (server != null) { try { server.stop(); } catch (Exception x) { LOG.ignore(x); } } throw e; } finally { Thread.currentThread().setContextClassLoader(contextCl); } } /* ------------------------------------------------------------ */ public void stop() { try { if (_server.isRunning()) { _server.stop(); } } catch (Exception e) { LOG.warn(e); } } /* ------------------------------------------------------------ */ /** * Must be called after the server is configured. * * It is assumed the server has already been configured with the ContextHandlerCollection structure. * */ private void init() { // Get the context handler _ctxtCollection = (ContextHandlerCollection) _server.getChildHandlerByClass(ContextHandlerCollection.class); if (_ctxtCollection == null) throw new IllegalStateException("ERROR: No ContextHandlerCollection configured in Server"); List<String> providerClassNames = new ArrayList<String>(); // get a deployerManager and some providers Collection<DeploymentManager> deployers = _server.getBeans(DeploymentManager.class); if (deployers != null && !deployers.isEmpty()) { _deploymentManager = deployers.iterator().next(); for (AppProvider provider : _deploymentManager.getAppProviders()) { providerClassNames.add(provider.getClass().getName()); } } else { //add some kind of default _deploymentManager = new DeploymentManager(); _deploymentManager.setContexts(_ctxtCollection); _server.addBean(_deploymentManager); } _deploymentManager.setUseStandardBindings(false); List<AppLifeCycle.Binding> deploymentLifeCycleBindings = new ArrayList<AppLifeCycle.Binding>(); deploymentLifeCycleBindings.add(new OSGiDeployer(this)); deploymentLifeCycleBindings.add(new StandardStarter()); deploymentLifeCycleBindings.add(new StandardStopper()); deploymentLifeCycleBindings.add(new OSGiUndeployer(this)); _deploymentManager.setLifeCycleBindings(deploymentLifeCycleBindings); if (!providerClassNames.contains(BundleWebAppProvider.class.getName())) { // create it on the fly with reasonable default values. try { BundleWebAppProvider webAppProvider = new BundleWebAppProvider(this); _deploymentManager.addAppProvider(webAppProvider); } catch (Exception e) { LOG.warn(e); } } if (!providerClassNames.contains(ServiceWebAppProvider.class.getName())) { // create it on the fly with reasonable default values. try { ServiceWebAppProvider webAppProvider = new ServiceWebAppProvider(this); _deploymentManager.addAppProvider(webAppProvider); } catch (Exception e) { LOG.warn(e); } } if (!providerClassNames.contains(BundleContextProvider.class.getName())) { try { BundleContextProvider contextProvider = new BundleContextProvider(this); _deploymentManager.addAppProvider(contextProvider); } catch (Exception e) { LOG.warn(e); } } if (!providerClassNames.contains(ServiceContextProvider.class.getName())) { try { ServiceContextProvider contextProvider = new ServiceContextProvider(this); _deploymentManager.addAppProvider(contextProvider); } catch (Exception e) { LOG.warn(e); } } } /* ------------------------------------------------------------ */ /** * Get the folders that might contain jars for the legacy J2EE shared * libraries */ private List<File> extractFiles(String propertyValue) { StringTokenizer tokenizer = new StringTokenizer(propertyValue, ",;", false); List<File> files = new ArrayList<File>(); while (tokenizer.hasMoreTokens()) { String tok = tokenizer.nextToken(); try { URL url = new URL(tok); url = BundleFileLocatorHelperFactory.getFactory().getHelper().getFileURL(url); if (url.getProtocol().equals("file")) { Resource res = Resource.newResource(url); File folder = res.getFile(); if (folder != null) { files.add(folder); } } } catch (Throwable mfe) { LOG.warn(mfe); } } return files; } }