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:
parent
103bdc47c3
commit
5e2110d894
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -347,8 +347,6 @@ public class WebappRegistrationHelper
|
|||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
_server.start();
|
||||
}
|
||||
catch (Throwable t)
|
||||
|
|
Loading…
Reference in New Issue