| // |
| // ======================================================================== |
| // Copyright (c) 1995-2016 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.io.InputStream; |
| 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.IO; |
| 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; |
| import org.xml.sax.SAXParseException; |
| |
| /** |
| * 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) |
| { |
| InputStream is = null; |
| try |
| { |
| // Execute a Jetty configuration file |
| Resource r = Resource.newResource(jettyConfiguration); |
| if (!r.exists()) |
| { |
| LOG.warn("File does not exist "+r); |
| throw new IllegalStateException("No such jetty server config file: "+r); |
| } |
| is = r.getInputStream(); |
| XmlConfiguration config = new XmlConfiguration(is); |
| 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 (SAXParseException saxparse) |
| { |
| LOG.warn("Unable to configure the jetty/etc file " + jettyConfiguration, saxparse); |
| throw saxparse; |
| } |
| finally |
| { |
| IO.close(is); |
| } |
| } |
| |
| 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 |
| */ |
| 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; |
| } |
| |
| } |