blob: cd0ad0e27a753e0434c523b165d14a234fa74ed1 [file] [log] [blame]
//
// ========================================================================
// 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.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;
}
}