in progress: support for multiple instances of jetty

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@2029 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Hugues Malphettes 2010-06-20 01:39:24 +00:00
parent 103bdc47c3
commit 5e2110d894
6 changed files with 433 additions and 14 deletions

View File

@ -30,7 +30,6 @@ import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.Configuration;
@ -321,7 +320,7 @@ public class JettyBootstrapActivator implements BundleActivator
}
properties.put(OSGiWebappConstants.MANAGED_JETTY_JETTY_XML_URL, actualBundleUrls.toString());
properties.put(OSGiWebappConstants.MANAGED_JETTY_XML_CONFIG_URLS, actualBundleUrls.toString());
configuration.update(properties);
}

View File

@ -98,6 +98,6 @@ public class OSGiWebappConstants
/**
* List of URLs to the folders where the legacy J2EE shared libraries are stored aka lib/ext, lib/jsp etc.
*/
public static final String MANAGED_JETTY_SHARED_LIB_URLS = "managedJettySharedLibUrls";
public static final String MANAGED_JETTY_SHARED_LIB_FOLDER_URLS = "managedJettySharedLibFolderUrls";
}

View File

@ -83,7 +83,7 @@ public class JettyServersManagedFactory implements ManagedServiceFactory
/**
* Servers indexed by PIDs. PIDs are generated by the ConfigurationAdmin service.
*/
private Map<String, Server> _serversIndexedByPID = new HashMap<String, Server>();
private Map<String, ServerInstanceWrapper> _serversIndexedByPID = new HashMap<String, ServerInstanceWrapper>();
/**
* PID -> {@link OSGiWebappConstants#MANAGED_JETTY_SERVER_NAME}
*/
@ -105,29 +105,29 @@ public class JettyServersManagedFactory implements ManagedServiceFactory
public void updated(String pid, Dictionary properties) throws ConfigurationException
{
Server server = getServerByPID(pid);
ServerInstanceWrapper serverInstanceWrapper = getServerByPID(pid);
deleted(pid);
// do we need to collect the currently deployed http services and
// webapps
// to be able to re-deploy them later?
// probably not. simply restart and see the various service trackers
// do everything that is needed.
server = new Server();
_serversIndexedByPID.put(pid, server);
String name = (String)properties.get(OSGiWebappConstants.MANAGED_JETTY_SERVER_NAME);
if (name == null)
{
throw new ConfigurationException(OSGiWebappConstants.MANAGED_JETTY_SERVER_NAME,
"The name of the server is mandatory");
}
serverInstanceWrapper = new ServerInstanceWrapper(name);
_serversIndexedByPID.put(pid, serverInstanceWrapper);
_serversNameIndexedByPID.put(pid, name);
_serversPIDIndexedByName.put(name, pid);
ServerInstanceWrapperHelper.start(server, pid, properties);
serverInstanceWrapper.start(new Server(), properties);
}
public synchronized void deleted(String pid)
{
Server server = (Server)_serversIndexedByPID.remove(pid);
ServerInstanceWrapper server = (ServerInstanceWrapper)_serversIndexedByPID.remove(pid);
String name = _serversNameIndexedByPID.remove(pid);
if (name != null)
{
@ -141,7 +141,7 @@ public class JettyServersManagedFactory implements ManagedServiceFactory
{
try
{
ServerInstanceWrapperHelper.stop(server, pid);
server.stop();
}
catch (Exception e)
{
@ -150,12 +150,12 @@ public class JettyServersManagedFactory implements ManagedServiceFactory
}
}
public synchronized Server getServerByPID(String pid)
public synchronized ServerInstanceWrapper getServerByPID(String pid)
{
return _serversIndexedByPID.get(pid);
}
public synchronized Server getServerByName(String name)
public synchronized ServerInstanceWrapper getServerByName(String name)
{
String pid = _serversPIDIndexedByName.get(name);
return pid != null ? _serversIndexedByPID.get(pid) : null;

View File

@ -0,0 +1,374 @@
// ========================================================================
// Copyright (c) 2009 Intalio, Inc.
// ------------------------------------------------------------------------
// 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.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.internal.jsp.TldLocatableURLClassloader;
import org.eclipse.jetty.osgi.boot.internal.webapp.LibExtClassLoaderHelper;
import org.eclipse.jetty.osgi.boot.internal.webapp.WebappRegistrationHelper;
import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
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;
/**
* Exposes a Jetty Server to be managed by an OSGi ManagedServiceFactory
* Configure and start it.
* Can also be used outside of the ManagedServiceFactory
*/
public class ServerInstanceWrapper {
private static Logger __logger = Log.getLogger(ServerInstanceWrapper.class.getName());
private final String _managedServerName;
/**
* The managed jetty server
*/
private Server _server;
private ContextHandlerCollection _ctxtHandler;
/**
* 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;
private OSGiAppProvider _provider;
public ServerInstanceWrapper(String managedServerName)
{
_managedServerName = managedServerName;
}
public void start(Server server, Dictionary props)
{
_server = server;
ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
try
{
// passing this bundle's classloader as the context classlaoder
// makes sure there is access to all the jetty's bundles
URLClassLoader libExtClassLoader = null;
String sharedURLs = (String)props.get(OSGiWebappConstants.MANAGED_JETTY_SHARED_LIB_FOLDER_URLS);
try
{
List<File> shared = sharedURLs != null ? extractFiles(sharedURLs) : null;
libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader(
shared, null, server, JettyBootstrapActivator.class.getClassLoader());
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
Thread.currentThread().setContextClassLoader(libExtClassLoader);
configure(server, props);
init();
//now that we have an app provider we can call the registration customizer.
try
{
URL[] jarsWithTlds = getJarsWithTlds();
_commonParentClassLoaderForWebapps = jarsWithTlds == null
? libExtClassLoader
:new TldLocatableURLClassloader(libExtClassLoader,jarsWithTlds);
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
server.start();
}
catch (Throwable t)
{
t.printStackTrace();
}
finally
{
Thread.currentThread().setContextClassLoader(contextCl);
}
}
public void stop()
{
try {
_server.stop();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* TODO: right now only the jetty-jsp bundle is scanned for common taglibs.
* Should support a way to plug more bundles that contain taglibs.
*
* The jasper TldScanner expects a URLClassloader to parse a jar for the
* /META-INF/*.tld it may contain. We place the bundles that we know contain
* such tag-libraries. Please note that it will work if and only if the
* bundle is a jar (!) Currently we just hardcode the bundle that contains
* the jstl implemenation.
*
* A workaround when the tld cannot be parsed with this method is to copy
* and paste it inside the WEB-INF of the webapplication where it is used.
*
* Support only 2 types of packaging for the bundle: - the bundle is a jar
* (recommended for runtime.) - the bundle is a folder and contain jars in
* the root and/or in the lib folder (nice for PDE developement situations)
* Unsupported: the bundle is a jar that embeds more jars.
*
* @return
* @throws Exception
*/
private URL[] getJarsWithTlds() throws Exception
{
ArrayList<URL> res = new ArrayList<URL>();
for (WebappRegistrationCustomizer regCustomizer : WebappRegistrationHelper.JSP_REGISTRATION_HELPERS)
{
URL[] urls = regCustomizer.getJarsWithTlds(_provider, WebappRegistrationHelper.BUNDLE_FILE_LOCATOR_HELPER);
for (URL url : urls)
{
if (!res.contains(url))
res.add(url);
}
}
if (!res.isEmpty())
return res.toArray(new URL[res.size()]);
else
return null;
}
private void configure(Server server, Dictionary props) throws Exception
{
String jettyConfigurationUrls = (String) props.get(OSGiWebappConstants.MANAGED_JETTY_XML_CONFIG_URLS);
List<URL> jettyConfigurations = jettyConfigurationUrls != null
? extractResources(jettyConfigurationUrls) : null;
if (jettyConfigurations == null || jettyConfigurations.isEmpty())
{
return;
}
Map<Object,Object> id_map = new HashMap<Object,Object>();
id_map.put("Server",server);
Map<Object,Object> properties = new HashMap<Object,Object>();
Enumeration en = props.keys();
while (en.hasMoreElements())
{
Object key = en.nextElement();
Object value = props.get(key);
properties.put(key, value);
}
for (URL jettyConfiguration : jettyConfigurations)
{
InputStream is = null;
try
{
// Execute a Jetty configuration file
is = jettyConfiguration.openStream();
XmlConfiguration config = new XmlConfiguration(is);
config.setIdMap(id_map);
config.setProperties(properties);
config.configure();
id_map=config.getIdMap();
}
catch (SAXParseException saxparse)
{
Log.getLogger(WebappRegistrationHelper.class.getName())
.warn("Unable to configure the jetty/etc file " + jettyConfiguration,saxparse);
throw saxparse;
}
finally
{
IO.close(is);
}
}
}
/**
* Must be called after the server is configured.
*
* Locate the actual instance of the ContextDeployer and WebAppDeployer that
* was created when configuring the server through jetty.xml. If there is no
* such thing it won't be possible to deploy webapps from a context and we
* throw IllegalStateExceptions.
*/
private void init()
{
// Get the context handler
_ctxtHandler = (ContextHandlerCollection)_server.getChildHandlerByClass(ContextHandlerCollection.class);
// get a deployerManager
List<DeploymentManager> deployers = _server.getBeans(DeploymentManager.class);
if (deployers != null && !deployers.isEmpty())
{
_deploymentManager = deployers.get(0);
for (AppProvider provider : _deploymentManager.getAppProviders())
{
if (provider instanceof OSGiAppProvider)
{
_provider=(OSGiAppProvider)provider;
break;
}
}
if (_provider == null)
{
//create it on the fly with reasonable default values.
try
{
_provider = new OSGiAppProvider();
_provider.setMonitoredDir(
Resource.newResource(getDefaultOSGiContextsHome(
new File(System.getProperty("jetty.home"))).toURI()));
} catch (IOException e) {
e.printStackTrace();
}
_deploymentManager.addAppProvider(_provider);
}
}
if (_ctxtHandler == null || _provider==null)
throw new IllegalStateException("ERROR: No ContextHandlerCollection or OSGiAppProvider configured");
}
/**
* @return The default folder in which the context files of the osgi bundles
* are located and watched. Or null when the system property
* "jetty.osgi.contexts.home" is not defined.
* If the configuration file defines the OSGiAppProvider's context.
* This will not be taken into account.
*/
File getDefaultOSGiContextsHome(File jettyHome)
{
String jettyContextsHome = System.getProperty("jetty.osgi.contexts.home");
if (jettyContextsHome != null)
{
File contextsHome = new File(jettyContextsHome);
if (!contextsHome.exists() || !contextsHome.isDirectory())
{
throw new IllegalArgumentException("the ${jetty.osgi.contexts.home} '" + jettyContextsHome + " must exist and be a folder");
}
return contextsHome;
}
return new File(jettyHome, "/contexts");
}
File getOSGiContextsHome()
{
return _provider.getContextXmlDirAsFile();
}
/**
* @return the urls in this string.
*/
private List<URL> extractResources(String propertyValue)
{
StringTokenizer tokenizer = new StringTokenizer(propertyValue, ",;", false);
List<URL> urls = new ArrayList<URL>();
while (tokenizer.hasMoreTokens())
{
String tok = tokenizer.nextToken();
try
{
urls.add(((DefaultFileLocatorHelper) WebappRegistrationHelper
.BUNDLE_FILE_LOCATOR_HELPER).getLocalURL(new URL(tok)));
}
catch (Throwable mfe)
{
}
}
return urls;
}
/**
* 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 = ((DefaultFileLocatorHelper) WebappRegistrationHelper
.BUNDLE_FILE_LOCATOR_HELPER).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)
{
}
}
return files;
}
}

View File

@ -19,6 +19,7 @@ import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -92,6 +93,18 @@ public class LibExtClassLoaderHelper
* @throws MalformedURLException
*/
public static URLClassLoader createLibEtcClassLoaderHelper(File jettyHome, Server server, ClassLoader parentClassLoader) throws MalformedURLException
{
return null;
}
/**
* @param server
* @return a url classloader with the jars of resources, lib/ext and the
* jars passed in the other argument. The parent classloader usually
* is the JettyBootStrapper (an osgi classloader.
* @throws MalformedURLException
*/
public static URLClassLoader createLibEtcClassLoader(File jettyHome, Server server,
ClassLoader parentClassLoader) throws MalformedURLException
{
ArrayList<URL> urls = new ArrayList<URL>();
@ -138,6 +151,41 @@ public class LibExtClassLoaderHelper
return new URLClassLoader(urls.toArray(new URL[urls.size()]),parentClassLoader);
}
/**
* @param server
* @return a url classloader with the jars of resources, lib/ext and the
* jars passed in the other argument. The parent classloader usually
* is the JettyBootStrapper (an osgi classloader.
* @throws MalformedURLException
*/
public static URLClassLoader createLibExtClassLoader(List<File> jarsContainerOrJars,
List<URL> otherJarsOrFolder, Server server,
ClassLoader parentClassLoader) throws MalformedURLException
{
List<URL> urls = new ArrayList<URL>(otherJarsOrFolder);
for (File libExt : jarsContainerOrJars)
{
if (libExt.isDirectory())
{
for (File f : libExt.listFiles())
{
if (f.getName().endsWith(".jar"))
{
// cheap to tolerate folders so let's do it.
URL url = f.toURI().toURL();
if (f.isFile())
{// is this necessary anyways?
url = new URL("jar:" + url.toString() + "!/");
}
urls.add(url);
}
}
}
}
return new URLClassLoader(urls.toArray(new URL[urls.size()]),parentClassLoader);
}
/**
* When we find files typically used for central logging configuration we do
* what it takes in this method to do what the user expects. Without

View File

@ -347,8 +347,6 @@ public class WebappRegistrationHelper
{
e.printStackTrace();
}
_server.start();
}
catch (Throwable t)