// // ======================================================================== // 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.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Dictionary; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import java.util.StringTokenizer; import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator; import org.eclipse.jetty.osgi.boot.OSGiServerConstants; import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory; import org.eclipse.jetty.osgi.boot.utils.OSGiClassLoader; import org.eclipse.jetty.osgi.boot.utils.Util; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.JarResource; import org.eclipse.jetty.util.resource.Resource; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; /** * DefaultJettyAtJettyHomeHelper * <p> * Creates a default instance of Jetty, based on the values of the * System properties "jetty.home" or "jetty.home.bundle", one of which * must be specified in order to create the default instance. * <p> * Called by the {@link JettyBootstrapActivator} during the starting of the * bundle. */ public class DefaultJettyAtJettyHomeHelper { private static final Logger LOG = Log.getLogger(DefaultJettyAtJettyHomeHelper.class); /** * contains a comma separated list of paths to the etc/jetty-*.xml files */ public static final String JETTY_ETC_FILES = OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS; /** * Set of config files to apply to a jetty Server instance if none are supplied by SYS_PROP_JETTY_ETC_FILES */ public static final String DEFAULT_JETTY_ETC_FILES = "etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml"; /** * Default location within bundle of a jetty home dir. */ public static final String DEFAULT_JETTYHOME = "/jettyhome"; /* ------------------------------------------------------------ */ /** * Called by the JettyBootStrapActivator. If the system property jetty.home * is defined and points to a folder, creates a corresponding jetty * server. * <p> * If the system property jetty.home.bundle is defined and points to a * bundle, look for the configuration of jetty inside that bundle. * </p> * <p> * In both cases reads the system property 'jetty.etc.config.urls' to locate * the configuration files for the deployed jetty. It is a comma separated * list of URLs or relative paths inside the bundle or folder to the config * files. * </p> * <p> * In both cases the system properties jetty.http.host, jetty.http.port and * jetty.ssl.port are passed to the configuration files that might use them * as part of their properties. * </p> * @param bundleContext the bundle context * @return the configured server * @throws Exception if unable to create / configure / or start the server */ public static Server startJettyAtJettyHome(BundleContext bundleContext) throws Exception { String jettyHomeSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME); String jettyHomeBundleSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME_BUNDLE); File jettyHomeDir = null; Bundle jettyHomeBundle = null; Dictionary<String,String> properties = new Hashtable<String,String>(); if (jettyHomeSysProp != null) { jettyHomeSysProp = Util.resolvePropertyValue(jettyHomeSysProp); // bug 329621 if (jettyHomeSysProp.startsWith("\"") && jettyHomeSysProp.endsWith("\"") || (jettyHomeSysProp.startsWith("'") && jettyHomeSysProp.endsWith("'"))) jettyHomeSysProp = jettyHomeSysProp.substring(1, jettyHomeSysProp.length() - 1); if (jettyHomeBundleSysProp != null) LOG.warn("Both jetty.home and jetty.home.bundle property defined: jetty.home.bundle ignored."); jettyHomeDir = new File(jettyHomeSysProp); if (!jettyHomeDir.exists() || !jettyHomeDir.isDirectory()) { LOG.warn("Unable to locate the jetty.home folder " + jettyHomeSysProp); return null; } //set jetty.home Util.setProperty(properties, OSGiServerConstants.JETTY_HOME, jettyHomeDir.getAbsolutePath()); } else if (jettyHomeBundleSysProp != null) { jettyHomeBundleSysProp = Util.resolvePropertyValue(jettyHomeBundleSysProp); for (Bundle b : bundleContext.getBundles()) { if (b.getState() == Bundle.UNINSTALLED) continue; if (b.getSymbolicName().equals(jettyHomeBundleSysProp)) { jettyHomeBundle = b; break; } } if (jettyHomeBundle == null) { LOG.warn("Unable to find the jetty.home.bundle named " + jettyHomeSysProp); return null; } } if (jettyHomeDir == null && jettyHomeBundle == null) { LOG.warn("No default jetty created."); return null; } //configure the server here rather than letting the JettyServerServiceTracker do it, because we want to be able to //configure the ThreadPool, which can only be done via the constructor, ie from within the xml configuration processing List<URL> configURLs = jettyHomeDir != null ? getJettyConfigurationURLs(jettyHomeDir) : getJettyConfigurationURLs(jettyHomeBundle, properties); LOG.info("Configuring the default jetty server with {}",configURLs); String home=properties.get(OSGiServerConstants.JETTY_HOME); String base=properties.get(OSGiServerConstants.JETTY_BASE); if (base==null) base=home; LOG.info("JETTY.HOME="+home); LOG.info("JETTY.BASE="+base); ClassLoader contextCl = Thread.currentThread().getContextClassLoader(); try { ClassLoader cl; if (jettyHomeBundle != null) { cl = new OSGiClassLoader(JettyBootstrapActivator.class.getClassLoader(),jettyHomeBundle); } else { cl = JettyBootstrapActivator.class.getClassLoader(); } Thread.currentThread().setContextClassLoader(cl); // these properties usually are the ones passed to this type of // configuration. properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME); Util.setProperty(properties, OSGiServerConstants.JETTY_HOST, System.getProperty(OSGiServerConstants.JETTY_HOST, System.getProperty("jetty.host"))); Util.setProperty(properties, OSGiServerConstants.JETTY_PORT, System.getProperty(OSGiServerConstants.JETTY_PORT, System.getProperty("jetty.port"))); Util.setProperty(properties, OSGiServerConstants.JETTY_PORT_SSL, System.getProperty(OSGiServerConstants.JETTY_PORT_SSL, System.getProperty("ssl.port"))); Util.setProperty(properties, OSGiServerConstants.JETTY_HOME, home); Util.setProperty(properties, OSGiServerConstants.JETTY_BASE, base); Server server = ServerInstanceWrapper.configure(null, configURLs, properties); //Register the default Server instance as an OSGi service. //The JettyServerServiceTracker will notice it and set it up to deploy bundles as wars etc bundleContext.registerService(Server.class.getName(), server, properties); LOG.info("Default jetty server configured"); return server; } catch (Exception e) { LOG.warn(e); throw e; } finally { Thread.currentThread().setContextClassLoader(contextCl); } } /* ------------------------------------------------------------ */ /** * Minimum setup for the location of the configuration files given a * jettyhome folder. Reads the system property jetty.etc.config.urls and * look for the corresponding jetty configuration files that will be used to * setup the jetty server. * * @param jettyhome * @return * @throws MalformedURLException */ private static List<URL> getJettyConfigurationURLs(File jettyhome) throws MalformedURLException { List<URL> configURLs = new ArrayList<URL>(); String jettyetc = System.getProperty(JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES); StringTokenizer tokenizer = new StringTokenizer(jettyetc, ";,", false); while (tokenizer.hasMoreTokens()) { String next = tokenizer.nextToken().trim(); //etc files can either be relative to jetty.home or absolute disk locations if (!next.startsWith("/") && (next.indexOf(':') == -1)) configURLs.add(new File(jettyhome, next).toURI().toURL()); else configURLs.add(new URL(next)); } return configURLs; } /* ------------------------------------------------------------ */ /** * Minimum setup for the location of the configuration files given a * configuration embedded inside a bundle. Reads the system property * jetty.etc.config.urls and look for the corresponding jetty configuration * files that will be used to setup the jetty server. * * @param jettyhome * @return */ private static List<URL> getJettyConfigurationURLs(Bundle configurationBundle, Dictionary properties) throws Exception { List<URL> configURLs = new ArrayList<URL>(); String files = System.getProperty(JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES); StringTokenizer tokenizer = new StringTokenizer(files, ";,", false); while (tokenizer.hasMoreTokens()) { String etcFile = tokenizer.nextToken().trim(); //file path is absolute if (etcFile.startsWith("/") || etcFile.indexOf(":") != -1) configURLs.add(new URL(etcFile)); else //relative file path { Enumeration<URL> enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, etcFile); String home = null; // default for org.eclipse.osgi.boot where we look inside // jettyhome/ for the default embedded configuration. if ((enUrls == null || !enUrls.hasMoreElements())) { home = DEFAULT_JETTYHOME; String tmp = DEFAULT_JETTYHOME+(DEFAULT_JETTYHOME.endsWith("/")?"":"/")+etcFile; enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, tmp); LOG.info("Configuring jetty from bundle: {} with {}", configurationBundle.getSymbolicName(),tmp); } //lazily ensure jetty.home value is set based on location of etc files if (properties.get(OSGiServerConstants.JETTY_HOME) == null) { Resource res = findDir(configurationBundle, home); if (res != null) properties.put(OSGiServerConstants.JETTY_HOME, res.toString()); } if (enUrls == null || !enUrls.hasMoreElements()) throw new IllegalStateException ("Unable to locate a jetty configuration file for " + etcFile); URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getFileURL(enUrls.nextElement()); configURLs.add(url); } } return configURLs; } /* ------------------------------------------------------------ */ /** * Get a resource representing a directory inside a bundle. If the dir is null, * return a resource representing the installation location of the bundle. * @param bundle the bundle * @param dir the directory * @return the resource found */ public static Resource findDir (Bundle bundle, String dir) { if (bundle == null) return null; try { File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); URL u = f.toURI().toURL(); u = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(u); Resource res = Resource.newResource(u); String s = res.toString(); //check if it is an unarchived bundle if (s.endsWith(".jar") && s.startsWith("file:")) res = JarResource.newJarResource(res); //if looking for a directory if (dir != null) res = res.addPath(dir); return res; } catch (Exception e) { LOG.warn("Bad bundle location" , e); return null; } } }