Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot')
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java111
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiAppProvider.java45
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiServerConstants.java51
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java12
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloaderWithInsertedJettyClassloader.java2
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java253
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/IManagedJettyServerRegistry.java28
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java161
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServersManagedFactory.java149
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java422
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/IWebBundleDeployerHelper.java84
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java214
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyHomeHelper.java272
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java55
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java28
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java619
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java (renamed from jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerExtender.java)143
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebappRegistrationHelper.java979
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java15
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java2
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java5
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java129
22 files changed, 2320 insertions, 1459 deletions
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
index 1c7250fcef..a7484296bf 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
@@ -18,8 +18,11 @@ import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Properties;
-import org.eclipse.jetty.osgi.boot.internal.webapp.JettyContextHandlerExtender;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.JettyServerServiceTracker;
+import org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper;
import org.eclipse.jetty.osgi.boot.internal.webapp.JettyContextHandlerServiceTracker;
+import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleTrackerCustomizer;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
@@ -27,8 +30,8 @@ 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.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.BundleTracker;
/**
* Experiment: bootstrap jetty's complete distrib from an OSGi bundle. Progress:
@@ -65,6 +68,11 @@ public class JettyBootstrapActivator implements BundleActivator
private Server _server;
private JettyContextHandlerServiceTracker _jettyContextHandlerTracker;
private PackageAdminServiceTracker _packageAdminServiceTracker;
+ private BundleTracker _webBundleTracker;
+
+// private ServiceRegistration _jettyServerFactoryService;
+ private JettyServerServiceTracker _jettyServerServiceTracker;
+
/**
* Setup a new jetty Server, registers it as a service. Setup the Service
@@ -82,25 +90,30 @@ public class JettyBootstrapActivator implements BundleActivator
// should activate.
_packageAdminServiceTracker = new PackageAdminServiceTracker(context);
- // todo: replace all this by the ManagedFactory so that we can start
- // multiple jetty servers.
- _server = new Server();
- // expose the server as a service.
- _registeredServer = context.registerService(_server.getClass().getName(),_server,new Properties());
+ _jettyServerServiceTracker = new JettyServerServiceTracker();
+ context.addServiceListener(_jettyServerServiceTracker,"(objectclass=" + Server.class.getName() + ")");
+
+ //Register the Jetty Server Factory as a ManagedServiceFactory:
+// Properties jettyServerMgdFactoryServiceProps = new Properties();
+// jettyServerMgdFactoryServiceProps.put("pid", OSGiWebappConstants.MANAGED_JETTY_SERVER_FACTORY_PID);
+// _jettyServerFactoryService = context.registerService(
+// ManagedServiceFactory.class.getName(), new JettyServersManagedFactory(),
+// jettyServerMgdFactoryServiceProps);
+
+ _jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(_jettyServerServiceTracker);
+
// the tracker in charge of the actual deployment
// and that will configure and start the jetty server.
- _jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(context,_server);
-
- // TODO: add a couple more checks on the properties?
- // kind of nice not to so we can debug what is missing easily.
context.addServiceListener(_jettyContextHandlerTracker,"(objectclass=" + ContextHandler.class.getName() + ")");
- // now ready to support the Extender pattern:
- JettyContextHandlerExtender jettyContexHandlerExtender = new JettyContextHandlerExtender();
- context.addBundleListener(jettyContexHandlerExtender);
-
- jettyContexHandlerExtender.init(context);
-
+ //see if we shoult start a default jetty instance right now.
+ DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context);
+
+ // now ready to support the Extender pattern:
+ _webBundleTracker = new BundleTracker(context,
+ Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer());
+ _webBundleTracker.open();
+
}
/*
@@ -113,32 +126,68 @@ public class JettyBootstrapActivator implements BundleActivator
{
try
{
+
+ if (_webBundleTracker != null)
+ {
+ _webBundleTracker.close();
+ _webBundleTracker = null;
+ }
if (_jettyContextHandlerTracker != null)
{
_jettyContextHandlerTracker.stop();
context.removeServiceListener(_jettyContextHandlerTracker);
+ _jettyContextHandlerTracker = null;
+ }
+ if (_jettyServerServiceTracker != null)
+ {
+ _jettyServerServiceTracker.stop();
+ context.removeServiceListener(_jettyServerServiceTracker);
+ _jettyServerServiceTracker = null;
}
if (_packageAdminServiceTracker != null)
{
_packageAdminServiceTracker.stop();
context.removeServiceListener(_packageAdminServiceTracker);
+ _packageAdminServiceTracker = null;
}
if (_registeredServer != null)
{
try
{
_registeredServer.unregister();
- _registeredServer = null;
}
catch (IllegalArgumentException ill)
{
// already unregistered.
}
+ finally
+ {
+ _registeredServer = null;
+ }
}
+// if (_jettyServerFactoryService != null)
+// {
+// try
+// {
+// _jettyServerFactoryService.unregister();
+// }
+// catch (IllegalArgumentException ill)
+// {
+// // already unregistered.
+// }
+// finally
+// {
+// _jettyServerFactoryService = null;
+// }
+// }
+
}
finally
{
- _server.stop();
+ if (_server != null)
+ {
+ _server.stop();
+ }
INSTANCE = null;
}
}
@@ -148,8 +197,8 @@ public class JettyBootstrapActivator implements BundleActivator
* registers it as an OSGi service. The tracker
* {@link JettyContextHandlerServiceTracker} will do the actual deployment.
*
- * @param context
- * The current bundle context
+ * @param contributor
+ * The bundle
* @param webappFolderPath
* The path to the root of the webapp. Must be a path relative to
* bundle; either an absolute path.
@@ -171,18 +220,15 @@ public class JettyBootstrapActivator implements BundleActivator
* registers it as an OSGi service. The tracker
* {@link JettyContextHandlerServiceTracker} will do the actual deployment.
*
- * @param context
- * The current bundle context
+ * @param contributor
+ * The bundle
* @param webappFolderPath
* The path to the root of the webapp. Must be a path relative to
* bundle; either an absolute path.
* @param contextPath
* The context path. Must start with "/"
- * @param thisBundleInstallationOverride
- * The location to a folder where the context file is located
- * This overrides the default behavior that consists of using the
- * location where the bundle is installed. Useful when in fact
- * the webapp contributed is not inside a bundle.
+ * @param dic
+ * TODO: parameter description
* @throws Exception
*/
public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath, Dictionary<String, String> dic) throws Exception
@@ -220,17 +266,15 @@ public class JettyBootstrapActivator implements BundleActivator
* @param contextFilePath
* The path to the file inside the bundle that defines the
* context.
- * @param thisBundleInstallationOverride
- * The location to a folder where the context file is located
- * This overrides the default behavior that consists of using the
- * location where the bundle is installed. Useful when in fact
- * the webapp contributed is not inside a bundle.
+ * @param dic
+ * TODO: parameter description
* @throws Exception
*/
public static void registerContext(Bundle contributor, String contextFilePath, Dictionary<String, String> dic) throws Exception
{
ContextHandler contextHandler = new ContextHandler();
dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH,contextFilePath);
+ dic.put(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE,Boolean.TRUE.toString());
contributor.getBundleContext().registerService(ContextHandler.class.getName(),contextHandler,dic);
}
@@ -238,5 +282,6 @@ public class JettyBootstrapActivator implements BundleActivator
{
// todo
}
+
} \ No newline at end of file
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiAppProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiAppProvider.java
index 9ddc27bc74..eb93a8f3f7 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiAppProvider.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiAppProvider.java
@@ -1,5 +1,5 @@
// ========================================================================
-// Copyright (c) 2009 Mortbay, Inc.
+// Copyright (c) 2009-2010 Mortbay, Inc.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
@@ -25,6 +25,8 @@ import org.eclipse.jetty.deploy.providers.ContextProvider;
import org.eclipse.jetty.deploy.providers.ScanningAppProvider;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.osgi.framework.Bundle;
/**
* AppProvider for OSGi. Supports the configuration of ContextHandlers and
@@ -43,7 +45,7 @@ import org.eclipse.jetty.util.resource.Resource;
public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
{
- private boolean _extractWars = false;
+ private boolean _extractWars = true;
private boolean _parentLoaderPriority = false;
private String _defaultsDescriptor;
private String _tldBundles;
@@ -91,8 +93,6 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
/**
* Default OSGiAppProvider consutructed when none are defined in the
* jetty.xml configuration.
- *
- * @param contextsDir
*/
public OSGiAppProvider()
{
@@ -111,7 +111,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
this();
setMonitoredDir(Resource.newResource(contextsDir.toURI()));
}
-
+
/**
* Returns the ContextHandler that was created by WebappRegistractionHelper
*
@@ -140,19 +140,39 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
super.setDeploymentManager(deploymentManager);
}
+ private static String getOriginId(Bundle contributor, String pathInBundle)
+ {
+ return contributor.getSymbolicName() + "-" + contributor.getVersion().toString() +
+ (pathInBundle.startsWith("/") ? pathInBundle : "/" + pathInBundle);
+ }
+
+ /**
+ * @param context
+ * @throws Exception
+ */
+ public void addContext(Bundle contributor, String pathInBundle, ContextHandler context) throws Exception
+ {
+ addContext(getOriginId(contributor, pathInBundle), context);
+ }
/**
* @param context
* @throws Exception
*/
- public void addContext(ContextHandler context) throws Exception
+ public void addContext(String originId, ContextHandler context) throws Exception
{
// TODO apply configuration specific to this provider
+ if (context instanceof WebAppContext)
+ {
+ ((WebAppContext)context).setExtractWAR(isExtract());
+ }
// wrap context as an App
- App app = new App(getDeploymentManager(),this,context.getDisplayName(),context);
+ App app = new App(getDeploymentManager(),this,originId,context);
getDeployedApps().put(context.getDisplayName(),app);
getDeploymentManager().addApp(app);
}
+
+
/**
* Called by the scanner of the context files directory. If we find the
@@ -274,6 +294,17 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
return null;
}
}
+
+ public boolean isExtract()
+ {
+ return _extractWars;
+ }
+
+ public void setExtract(boolean extract)
+ {
+ _extractWars=extract;
+ }
+
/* ------------------------------------------------------------ */
/**
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiServerConstants.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiServerConstants.java
new file mode 100644
index 0000000000..c2bddc290c
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiServerConstants.java
@@ -0,0 +1,51 @@
+// ========================================================================
+// 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.
+// Contributors:
+// Hugues Malphettes - initial API and implementation
+// ========================================================================
+package org.eclipse.jetty.osgi.boot;
+
+/**
+ * Name of the properties that configure a jetty Server OSGi service.
+ */
+public class OSGiServerConstants
+{
+ //for managed jetty instances, name of the configuration parameters
+ /**
+ * PID of the jetty servers's ManagedFactory
+ */
+ public static final String MANAGED_JETTY_SERVER_FACTORY_PID = "org.eclipse.jetty.osgi.boot.managedserverfactory";
+
+ /**
+ * The associated value of that configuration parameter is the name under which this
+ * instance of the jetty server is tracked.
+ * When a ContextHandler is deployed and it specifies the managedServerName property, it is deployed
+ * on the corresponding jetty managed server or it throws an exception: jetty server not available.
+ */
+ public static final String MANAGED_JETTY_SERVER_NAME = "managedServerName";
+ /**
+ * Name of the 'default' jetty server instance.
+ * Usually the first one to be created.
+ */
+ public static final String MANAGED_JETTY_SERVER_DEFAULT_NAME = "defaultJettyServer";
+
+ /**
+ * List of URLs to the jetty.xml files that configure th server.
+ */
+ public static final String MANAGED_JETTY_XML_CONFIG_URLS = "jetty.etc.config.urls";
+
+ /**
+ * 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_FOLDER_URLS = "managedJettySharedLibFolderUrls";
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java
index c6c35d74f8..c6abc47c5b 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java
@@ -15,7 +15,7 @@
package org.eclipse.jetty.osgi.boot;
/**
- *
+ * Name of the service properties for a ContextHandler that configure a webapp deployed on jetty OSGi.
*/
public class OSGiWebappConstants
{
@@ -66,13 +66,5 @@ public class OSGiWebappConstants
* location if not null useful to install webapps or jetty context files
* that are in fact not embedded in a bundle
*/
- public static final String SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE = "thisBundleInstall";
-
- // sys prop config of jetty:
- /**
- * contains a comma separated list of pathes to the etc/jetty-*.xml files
- * used to configure jetty. By default the value is 'etc/jetty.xml' when the
- * path is relative the file is resolved relatively to jettyhome.
- */
- public static final String SYS_PROP_JETTY_ETC_FILES = "jetty.etc.files";
+ public static final String SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE = "thisBundleInstall";
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloaderWithInsertedJettyClassloader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloaderWithInsertedJettyClassloader.java
index 67104b7173..9c989c7f7e 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloaderWithInsertedJettyClassloader.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloaderWithInsertedJettyClassloader.java
@@ -30,7 +30,7 @@ public class TldLocatableURLClassloaderWithInsertedJettyClassloader extends TldL
/**
*
- * @param osgiClassLoader
+ * @param osgiClassLoaderParent
* The parent classloader
* @param internalClassLoader
* The classloader that will be at the same level than the
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
new file mode 100644
index 0000000000..7456cba037
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
@@ -0,0 +1,253 @@
+// ========================================================================
+// Copyright (c) 2010 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.
+// Contributors:
+// Hugues Malphettes - initial API and implementation
+// ========================================================================
+package org.eclipse.jetty.osgi.boot.internal.serverfactory;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Properties;
+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.BundleFileLocatorHelper;
+import org.eclipse.jetty.server.Server;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Called by the {@link JettyBootstrapActivator} during the starting of the bundle.
+ * If the system property 'jetty.home' is defined and points to a folder,
+ * then setup the corresponding jetty server and starts it.
+ */
+public class DefaultJettyAtJettyHomeHelper {
+
+ /**
+ * contains a comma separated list of pathes to the etc/jetty-*.xml files
+ * used to configure jetty. By default the value is 'etc/jetty.xml' when the
+ * path is relative the file is resolved relatively to jettyhome.
+ */
+ public static final String SYS_PROP_JETTY_ETC_FILES = OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS;
+
+ /**
+ * Usual system property used as the hostname for a typical jetty configuration.
+ */
+ public static final String SYS_PROP_JETTY_HOME = "jetty.home";
+ /**
+ * System property to point to a bundle that embeds a jetty configuration
+ * and that jetty configuration should be the default jetty server.
+ * First we look for jetty.home. If we don't find it then we look for this property.
+ */
+ public static final String SYS_PROP_JETTY_HOME_BUNDLE = "jetty.home.bundle";
+ /**
+ * Usual system property used as the hostname for a typical jetty configuration.
+ */
+ public static final String SYS_PROP_JETTY_HOST = "jetty.host";
+ /**
+ * Usual system property used as the port for http for a typical jetty configuration.
+ */
+ public static final String SYS_PROP_JETTY_PORT = "jetty.port";
+ /**
+ * Usual system property used as the port for https for a typical jetty configuration.
+ */
+ public static final String SYS_PROP_JETTY_PORT_SSL = "jetty.port.ssl";
+
+
+ /**
+ * Called by the JettyBootStrapActivator.
+ * If the system property jetty.home is defined and points to a folder,
+ * deploys the 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 and deploys the corresponding 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 spearate list of URLs or relative paths inside the bundle or folder
+ * to the config files. If underfined it defaults to 'etc/jetty.xml'.
+ * </p>
+ * <p>
+ * In both cases the system properties jetty.host, jetty.port and jetty.port.ssl are passed to the configuration files
+ * that might use them as part of their properties.
+ * </p>
+ */
+ public static void startJettyAtJettyHome(BundleContext bundleContext)
+ {
+ String jettyHomeSysProp = System.getProperty(SYS_PROP_JETTY_HOME);
+ String jettyHomeBundleSysProp = System.getProperty(SYS_PROP_JETTY_HOME_BUNDLE);
+ File jettyHome = null;
+ Bundle jettyHomeBundle = null;
+ if (jettyHomeSysProp != null)
+ {
+ if (jettyHomeBundleSysProp != null)
+ {
+ System.err.println("WARN: both the jetty.home property and the jetty.home.bundle property are defined."
+ + " jetty.home.bundle is not taken into account.");
+ }
+ jettyHome = new File(jettyHomeSysProp);
+ if (!jettyHome.exists() || !jettyHome.isDirectory())
+ {
+ System.err.println("Unable to locate the jetty.home folder " + jettyHomeSysProp);
+ return;
+ }
+ }
+ else if (jettyHomeBundleSysProp != null)
+ {
+ for (Bundle b : bundleContext.getBundles())
+ {
+ if (b.getSymbolicName().equals(jettyHomeBundleSysProp))
+ {
+ jettyHomeBundle = b;
+ break;
+ }
+ }
+ if (jettyHomeBundle == null)
+ {
+ System.err.println("Unable to find the jetty.home.bundle named " + jettyHomeSysProp);
+ return;
+ }
+
+ }
+ if (jettyHome == null && jettyHomeBundle == null)
+ {
+ System.err.println("No default jetty started.");
+ return;
+ }
+ try
+ {
+ Server server = new Server();
+ Properties properties = new Properties();
+ properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME);
+
+ String configURLs = jettyHome != null ? getJettyConfigurationURLs(jettyHome) : getJettyConfigurationURLs(jettyHomeBundle);
+ properties.put(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS, configURLs);
+
+ System.err.println("Configuring the default jetty server with " + configURLs);
+
+ //these properties usually are the ones passed to this type of configuration.
+ setProperty(properties,SYS_PROP_JETTY_HOME,System.getProperty(SYS_PROP_JETTY_HOME));
+ setProperty(properties,SYS_PROP_JETTY_HOST,System.getProperty(SYS_PROP_JETTY_HOST));
+ setProperty(properties,SYS_PROP_JETTY_PORT,System.getProperty(SYS_PROP_JETTY_PORT));
+ setProperty(properties,SYS_PROP_JETTY_PORT_SSL,System.getProperty(SYS_PROP_JETTY_PORT_SSL));
+
+ bundleContext.registerService(Server.class.getName(), server, properties);
+ }
+ catch (Throwable t)
+ {
+ t.printStackTrace();
+ }
+ }
+
+ /**
+ * 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
+ */
+ private static String getJettyConfigurationURLs(File jettyhome)
+ {
+ String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES,"etc/jetty.xml");
+ StringTokenizer tokenizer = new StringTokenizer(jettyetc,";,", false);
+ StringBuilder res = new StringBuilder();
+ while (tokenizer.hasMoreTokens())
+ {
+ String next = tokenizer.nextToken().trim();
+ if (!next.startsWith("/") && next.indexOf(':') == -1)
+ {
+ try {
+ next = new File(jettyhome, next).toURI().toURL().toString();
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ continue;
+ }
+ }
+ appendToCommaSeparatedList(res, next);
+ }
+ return res.toString();
+ }
+
+ /**
+ * 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 String getJettyConfigurationURLs(Bundle configurationBundle)
+ {
+ String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES,"etc/jetty.xml");
+ System.err.println("jettyetc=" + jettyetc);
+ StringTokenizer tokenizer = new StringTokenizer(jettyetc,";,", false);
+ StringBuilder res = new StringBuilder();
+
+ while (tokenizer.hasMoreTokens())
+ {
+ String etcFile = tokenizer.nextToken().trim();
+ if (etcFile.startsWith("/") || etcFile.indexOf(":") != -1)
+ {
+ appendToCommaSeparatedList(res, etcFile);
+ }
+ else
+ {
+ Enumeration<URL> enUrls = BundleFileLocatorHelper.DEFAULT
+ .findEntries(configurationBundle, etcFile);
+
+ //default for org.eclipse.osgi.boot where we look inside jettyhome for the default embedded configuration.
+ //default inside jettyhome. this way fragments to the bundle can define their own configuration.
+ if ((enUrls == null || !enUrls.hasMoreElements()) && etcFile.endsWith("etc/jetty.xml"))
+ {
+ enUrls = BundleFileLocatorHelper.DEFAULT
+ .findEntries(configurationBundle, "/jettyhome/etc/jetty-osgi-default.xml");
+ System.err.println("Configuring jetty with the default embedded configuration:" +
+ "bundle: " + configurationBundle.getSymbolicName() +
+ " config: /jettyhome/etc/jetty-osgi-default.xml");
+ }
+ if (enUrls == null || !enUrls.hasMoreElements())
+ {
+ System.err.println("Unable to locate a jetty configuration file for " + etcFile);
+ }
+ if (enUrls != null)
+ {
+ while (enUrls.hasMoreElements())
+ {
+ appendToCommaSeparatedList(res, enUrls.nextElement().toString());
+ }
+ }
+ }
+ }
+ return res.toString();
+ }
+
+ private static void appendToCommaSeparatedList(StringBuilder buffer, String value)
+ {
+ if (buffer.length() != 0)
+ {
+ buffer.append(",");
+ }
+ buffer.append(value);
+ }
+
+ private static void setProperty(Properties properties, String key, String value)
+ {
+ if (value != null)
+ {
+ properties.put(key, value);
+ }
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/IManagedJettyServerRegistry.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/IManagedJettyServerRegistry.java
new file mode 100644
index 0000000000..796447496d
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/IManagedJettyServerRegistry.java
@@ -0,0 +1,28 @@
+// ========================================================================
+// Copyright (c) 2010 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.
+// Contributors:
+// Hugues Malphettes - initial API and implementation
+// ========================================================================
+package org.eclipse.jetty.osgi.boot.internal.serverfactory;
+
+/**
+ * Keeps track of the running jetty servers. They are named.
+ */
+public interface IManagedJettyServerRegistry {
+
+ /**
+ * @param managedServerName The server name
+ * @return the corresponding jetty server wrapped with its deployment properties.
+ */
+ public ServerInstanceWrapper getServerInstanceWrapper(String managedServerName);
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java
new file mode 100644
index 0000000000..200647cecc
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java
@@ -0,0 +1,161 @@
+// ========================================================================
+// Copyright (c) 2009-2010 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.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
+import org.eclipse.jetty.server.Server;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Deploy the jetty server instances when they are registered as an OSGi service.
+ */
+public class JettyServerServiceTracker implements ServiceListener, IManagedJettyServerRegistry
+{
+
+ /**
+ * Servers indexed by PIDs. PIDs are generated by the ConfigurationAdmin service.
+ */
+ private Map<String, ServerInstanceWrapper> _serversIndexedByName = new HashMap<String, ServerInstanceWrapper>();
+ /** The context-handler to deactivate indexed by ServerInstanceWrapper */
+ private Map<ServiceReference, ServerInstanceWrapper> _indexByServiceReference = new HashMap<ServiceReference, ServerInstanceWrapper>();
+
+
+ /**
+ * Stops each one of the registered servers.
+ */
+ public void stop()
+ {
+ //not sure that this is really useful but here we go.
+ for (ServerInstanceWrapper wrapper : _serversIndexedByName.values())
+ {
+ try
+ {
+ wrapper.stop();
+ }
+ catch (Throwable t)
+ {
+
+ }
+ }
+ }
+
+
+ /**
+ * Receives notification that a service has had a lifecycle change.
+ *
+ * @param ev
+ * The <code>ServiceEvent</code> object.
+ */
+ public void serviceChanged(ServiceEvent ev)
+ {
+ ServiceReference sr = ev.getServiceReference();
+ switch (ev.getType())
+ {
+ case ServiceEvent.MODIFIED:
+ case ServiceEvent.UNREGISTERING:
+ {
+ ServerInstanceWrapper instance = unregisterInIndex(ev.getServiceReference());
+ if (instance != null)
+ {
+ try
+ {
+ instance.stop();
+ }
+ catch (Exception e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+ if (ev.getType() == ServiceEvent.UNREGISTERING)
+ {
+ break;
+ }
+ else
+ {
+ // modified, meaning: we reload it. now that we stopped it;
+ // we can register it.
+ }
+ case ServiceEvent.REGISTERED:
+ {
+ Bundle contributor = sr.getBundle();
+ Server server = (Server)contributor.getBundleContext().getService(sr);
+ ServerInstanceWrapper wrapper = registerInIndex(server, sr);
+ Properties props = new Properties();
+ for (String key : sr.getPropertyKeys())
+ {
+ Object value = sr.getProperty(key);
+ props.put(key, value);
+ }
+ wrapper.start(server, props);
+ break;
+ }
+ }
+ }
+
+ private ServerInstanceWrapper registerInIndex(Server server, ServiceReference sr)
+ {
+ String name = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
+ if (name == null)
+ {
+ throw new IllegalArgumentException("The property " +
+ OSGiServerConstants.MANAGED_JETTY_SERVER_NAME + " is mandatory");
+ }
+ ServerInstanceWrapper wrapper = new ServerInstanceWrapper(name);
+ _indexByServiceReference.put(sr,wrapper);
+ _serversIndexedByName.put(name,wrapper);
+ return wrapper;
+ }
+
+ /**
+ * Returns the ContextHandler to stop.
+ *
+ * @param reg
+ * @return the ContextHandler to stop.
+ */
+ private ServerInstanceWrapper unregisterInIndex(ServiceReference sr)
+ {
+ ServerInstanceWrapper handler = _indexByServiceReference.remove(sr);
+ if (handler == null)
+ {
+ // a warning?
+ return null;
+ }
+ String name = handler.getManagedServerName();
+ if (name != null)
+ {
+ _serversIndexedByName.remove(name);
+ }
+ return handler;
+ }
+
+ /**
+ * @param managedServerName The server name
+ * @return the corresponding jetty server wrapped with its deployment properties.
+ */
+ public ServerInstanceWrapper getServerInstanceWrapper(String managedServerName)
+ {
+ return _serversIndexedByName.get(managedServerName == null
+ ? OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME : managedServerName);
+ }
+
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServersManagedFactory.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServersManagedFactory.java
index d6b8aa9853..31ad22534b 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServersManagedFactory.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServersManagedFactory.java
@@ -14,45 +14,30 @@
// ========================================================================
package org.eclipse.jetty.osgi.boot.internal.serverfactory;
+import java.net.URL;
import java.util.Dictionary;
import java.util.HashMap;
+import java.util.Hashtable;
import java.util.Map;
+import java.util.StringTokenizer;
+import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
+import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.server.Server;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedServiceFactory;
/**
- * This is a work in progress. <br/>
- * In particular there is a lot of work required during the update of the
- * configuration of a server. It might not be practical to in fact support that
- * and re-deploy the webapps in the same state than before the server was
- * stopped.
- * <p>
- * jetty servers are managed as OSGi services registered here. try to find out
- * if a configuration will fail (ports already opened etc).
- * </p>
- * <p>
- * Try to enable the creation and configuration of jetty servers in all the
- * usual standard ways. The configuration of the server is defined by the
- * properties passed to the service:
- * <ol>
- * <li>First look for jettyfactory. If the value is a jetty server, use that
- * server</li>
- * <li>Then look for jettyhome key. The value should be a java.io.File or a
- * String that is a path to the folder It is required that a etc/jetty.xml file
- * will be loated from that folder.</li>
- * <li>Then look for a jettyxml key. The value should be a java.io.File or an
- * InputStream that contains a jetty configuration file.</li>
- * <li>TODO: More ways to configure a jetty server? (other IOCs like spring,
- * equinox properties...)</li>
- * <li>Throw an exception if none of the relevant parameters are found</li>
- * </ol>
- * </p>
+ * Manages the deployment of jetty server instances.
+ * Not sure this is bringing much compared to the JettyServerServiceTracker.
*
* @author hmalphettes
*/
-public class JettyServersManagedFactory implements ManagedServiceFactory
+public class JettyServersManagedFactory implements ManagedServiceFactory, IManagedJettyServerRegistry
{
/**
@@ -79,7 +64,18 @@ public class JettyServersManagedFactory implements ManagedServiceFactory
*/
public static final String JETTY_HTTPS_PORT = "jetty.http.port";
- private Map<String, Server> _servers = new HashMap<String, Server>();
+ /**
+ * Servers indexed by PIDs. PIDs are generated by the ConfigurationAdmin service.
+ */
+ private Map<String, ServerInstanceWrapper> _serversIndexedByPID = new HashMap<String, ServerInstanceWrapper>();
+ /**
+ * PID -> {@link OSGiWebappConstants#MANAGED_JETTY_SERVER_NAME}
+ */
+ private Map<String, String> _serversNameIndexedByPID = new HashMap<String, String>();
+ /**
+ * {@link OSGiWebappConstants#MANAGED_JETTY_SERVER_NAME} -> PID
+ */
+ private Map<String, String> _serversPIDIndexedByName = new HashMap<String, String>();
/**
* Return a descriptive name of this factory.
@@ -93,24 +89,43 @@ public class JettyServersManagedFactory implements ManagedServiceFactory
public void updated(String pid, Dictionary properties) throws ConfigurationException
{
- Server server = _servers.get(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.
-
+ String name = (String)properties.get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
+ if (name == null)
+ {
+ throw new ConfigurationException(OSGiServerConstants.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);
+ serverInstanceWrapper.start(new Server(), properties);
}
public synchronized void deleted(String pid)
{
- Server server = (Server)_servers.remove(pid);
+ ServerInstanceWrapper server = (ServerInstanceWrapper)_serversIndexedByPID.remove(pid);
+ String name = _serversNameIndexedByPID.remove(pid);
+ if (name != null)
+ {
+ _serversPIDIndexedByName.remove(name);
+ }
+ else
+ {
+ //something incorrect going on.
+ }
if (server != null)
{
try
{
- server.stop();
+ server.stop();
}
catch (Exception e)
{
@@ -119,4 +134,74 @@ public class JettyServersManagedFactory implements ManagedServiceFactory
}
}
+ public synchronized ServerInstanceWrapper getServerByPID(String pid)
+ {
+ return _serversIndexedByPID.get(pid);
+ }
+
+ /**
+ * @param managedServerName The server name
+ * @return the corresponding jetty server wrapped with its deployment properties.
+ */
+ public ServerInstanceWrapper getServerInstanceWrapper(String managedServerName)
+ {
+ String pid = _serversPIDIndexedByName.get(managedServerName);
+ return pid != null ? _serversIndexedByPID.get(pid) : null;
+ }
+
+ /**
+ * Helper method to create and configure a new Jetty Server via the ManagedServiceFactory
+ * @param contributor
+ * @param serverName
+ * @param urlsToJettyXml
+ * @throws Exception
+ */
+ public static void createNewServer(Bundle contributor, String serverName, String urlsToJettyXml) throws Exception
+ {
+ ServiceReference configurationAdminReference =
+ contributor.getBundleContext().getServiceReference( ConfigurationAdmin.class.getName() );
+
+ ConfigurationAdmin confAdmin = (ConfigurationAdmin) contributor.getBundleContext()
+ .getService( configurationAdminReference );
+
+ Configuration configuration = confAdmin.createFactoryConfiguration(
+ OSGiServerConstants.MANAGED_JETTY_SERVER_FACTORY_PID, contributor.getLocation() );
+ Dictionary properties = new Hashtable();
+ properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, serverName);
+
+ StringBuilder actualBundleUrls = new StringBuilder();
+ StringTokenizer tokenizer = new StringTokenizer(urlsToJettyXml, ",", false);
+ while (tokenizer.hasMoreTokens())
+ {
+ if (actualBundleUrls.length() != 0)
+ {
+ actualBundleUrls.append(",");
+ }
+ String token = tokenizer.nextToken();
+ if (token.indexOf(':') != -1)
+ {
+ //a complete url. no change needed:
+ actualBundleUrls.append(token);
+ }
+ else if (token.startsWith("/"))
+ {
+ //url relative to the contributor bundle:
+ URL url = contributor.getEntry(token);
+ if (url == null)
+ {
+ actualBundleUrls.append(token);
+ }
+ else
+ {
+ actualBundleUrls.append(url.toString());
+ }
+ }
+
+ }
+
+ properties.put(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS, actualBundleUrls.toString());
+ configuration.update(properties);
+
+ }
+
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java
new file mode 100644
index 0000000000..7e4afc5b29
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java
@@ -0,0 +1,422 @@
+// ========================================================================
+// 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.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+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.OSGiServerConstants;
+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.WebBundleDeployerHelper;
+import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
+import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
+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;
+
+
+/**
+ * Exposes a Jetty Server to be managed by an OSGi ManagedServiceFactory
+ * Configure and start it.
+ * Can also be used from 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;
+
+ private WebBundleDeployerHelper _webBundleDeployerHelper;
+
+
+ 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 OSGiAppProvider getOSGiAppProvider()
+ {
+ return _provider;
+ }
+
+
+ public Server getServer()
+ {
+ return _server;
+ }
+
+
+ public WebBundleDeployerHelper getWebBundleDeployerHelp()
+ {
+ return _webBundleDeployerHelper;
+ }
+
+ /**
+ * @return The collection of context handlers
+ */
+ public ContextHandlerCollection getContextHandlerCollection()
+ {
+ return _ctxtHandler;
+ }
+
+
+ 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
+ ClassLoader libExtClassLoader = null;
+ String sharedURLs = (String)props.get(OSGiServerConstants.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);
+ }
+ _webBundleDeployerHelper = new WebBundleDeployerHelper(this);
+ }
+
+
+ public void stop()
+ {
+ try {
+ if (_server.isRunning())
+ {
+ _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>();
+ WebBundleDeployerHelper.staticInit();//that is not looking great.
+ for (WebappRegistrationCustomizer regCustomizer : WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS)
+ {
+ URL[] urls = regCustomizer.getJarsWithTlds(_provider, WebBundleDeployerHelper.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(OSGiServerConstants.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)
+ {
+ __logger.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) WebBundleDeployerHelper
+ .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) WebBundleDeployerHelper
+ .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;
+ }
+
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/IWebBundleDeployerHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/IWebBundleDeployerHelper.java
new file mode 100644
index 0000000000..8805561480
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/IWebBundleDeployerHelper.java
@@ -0,0 +1,84 @@
+// ========================================================================
+// Copyright (c) 2010 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.
+// Contributors:
+// Hugues Malphettes - initial API and implementation
+// ========================================================================
+package org.eclipse.jetty.osgi.boot.internal.webapp;
+
+import org.eclipse.jetty.deploy.ContextDeployer;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.osgi.framework.Bundle;
+
+/**
+ * Internal interface for the class that deploys a webapp on a server.
+ * Used as we migrate from the single instance of the jety server to multiple jetty servers.
+ */
+public interface IWebBundleDeployerHelper {
+
+ /** when this property is present, the type of context handler registered is not
+ * known in advance. */
+ public static final String INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE = "unknownContextHandlerType";
+
+
+ /**
+ * Deploy a new web application on the jetty server.
+ *
+ * @param bundle
+ * The bundle
+ * @param webappFolderPath
+ * The path to the root of the webapp. Must be a path relative to
+ * bundle; either an absolute path.
+ * @param contextPath
+ * The context path. Must start with "/"
+ * @param extraClasspath
+ * @param overrideBundleInstallLocation
+ * @param webXmlPath
+ * @param defaultWebXmlPath
+ * TODO: parameter description
+ * @return The contexthandler created and started
+ * @throws Exception
+ */
+ public abstract WebAppContext registerWebapplication(Bundle bundle,
+ String webappFolderPath, String contextPath, String extraClasspath,
+ String overrideBundleInstallLocation, String webXmlPath,
+ String defaultWebXmlPath, WebAppContext webAppContext) throws Exception;
+
+ /**
+ * Stop a ContextHandler and remove it from the collection.
+ *
+ * @see ContextDeployer#undeploy
+ * @param contextHandler
+ * @throws Exception
+ */
+ public abstract void unregister(ContextHandler contextHandler)
+ throws Exception;
+
+ /**
+ * This type of registration relies on jetty's complete context xml file.
+ * Context encompasses jndi and all other things. This makes the definition
+ * of the webapp a lot more self-contained.
+ *
+ * @param contributor
+ * @param contextFileRelativePath
+ * @param extraClasspath
+ * @param overrideBundleInstallLocation
+ * @param handler the context handler passed in the server
+ * reference that will be configured, deployed and started.
+ * @return The contexthandler created and started
+ * @throws Exception
+ */
+ public abstract ContextHandler registerContext(Bundle contributor,
+ String contextFileRelativePath, String extraClasspath,
+ String overrideBundleInstallLocation, ContextHandler handler) throws Exception;
+
+} \ No newline at end of file
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java
index 153267b1d8..efb908239a 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java
@@ -13,12 +13,15 @@
package org.eclipse.jetty.osgi.boot.internal.webapp;
import java.io.File;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
+import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
-import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.IManagedJettyServerRegistry;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.webapp.WebAppContext;
@@ -49,62 +52,26 @@ import org.osgi.framework.ServiceReference;
public class JettyContextHandlerServiceTracker implements ServiceListener
{
- private final WebappRegistrationHelper _helper;
+ /** New style: ability to manage multiple jetty instances */
+ private final IManagedJettyServerRegistry _registry;
/** The context-handler to deactivate indexed by context handler */
private Map<ServiceReference, ContextHandler> _indexByServiceReference = new HashMap<ServiceReference, ContextHandler>();
/**
- * The index is the bundle-symbolic-name/paht/to/context/file when there is
- * such thing
+ * The index is the bundle-symbolic-name/path/to/context/file when there is such thing
*/
private Map<String, ServiceReference> _indexByContextFile = new HashMap<String, ServiceReference>();
- /** or null when */
- private String _osgiContextHomeFolderCanonicalPath;
/** in charge of detecting changes in the osgi contexts home folder. */
private Scanner _scanner;
/**
- * @param context
- * @param server
+ * @param registry
*/
- public JettyContextHandlerServiceTracker(BundleContext context, Server server) throws Exception
+ public JettyContextHandlerServiceTracker(IManagedJettyServerRegistry registry) throws Exception
{
- _helper = new WebappRegistrationHelper(server);
- _helper.setup(context,new HashMap<String, String>());
- File contextHome = _helper.getOSGiContextsHome();
- if (contextHome != null)
- {
- _osgiContextHomeFolderCanonicalPath = contextHome.getCanonicalPath();
- _scanner = new Scanner();
- _scanner.setRecursive(true);
- _scanner.setReportExistingFilesOnStartup(false);
- _scanner.addListener(new Scanner.DiscreteListener()
- {
- public void fileAdded(String filename) throws Exception
- {
- // adding a file does not create a new app,
- // it just reloads it with the new custom file.
- // well, if the file does not define a context handler,
- // then in fact it does remove it.
- reloadJettyContextHandler(filename);
- }
-
- public void fileChanged(String filename) throws Exception
- {
- reloadJettyContextHandler(filename);
- }
-
- public void fileRemoved(String filename) throws Exception
- {
- // removing a file does not remove the app:
- // it just goes back to the default embedded in the bundle.
- // well, if there was no default then it does remove it.
- reloadJettyContextHandler(filename);
- }
- });
- }
+ _registry = registry;
}
public void stop()
@@ -117,6 +84,49 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
// nothing to stop in the WebappRegistrationHelper
}
+
+ /**
+ * @param contextHome Parent folder where the context files can override the context files
+ * defined in the web bundles: equivalent to the contexts folder in a traditional
+ * jetty installation.
+ * when null, just do nothing.
+ */
+ protected void setupContextHomeScanner(File contextHome) throws IOException
+ {
+ if (contextHome == null)
+ {
+ return;
+ }
+ final String osgiContextHomeFolderCanonicalPath = contextHome.getCanonicalPath();
+ _scanner = new Scanner();
+ _scanner.setRecursive(true);
+ _scanner.setReportExistingFilesOnStartup(false);
+ _scanner.addListener(new Scanner.DiscreteListener()
+ {
+ public void fileAdded(String filename) throws Exception
+ {
+ // adding a file does not create a new app,
+ // it just reloads it with the new custom file.
+ // well, if the file does not define a context handler,
+ // then in fact it does remove it.
+ reloadJettyContextHandler(filename, osgiContextHomeFolderCanonicalPath);
+ }
+
+ public void fileChanged(String filename) throws Exception
+ {
+ reloadJettyContextHandler(filename, osgiContextHomeFolderCanonicalPath);
+ }
+
+ public void fileRemoved(String filename) throws Exception
+ {
+ // removing a file does not remove the app:
+ // it just goes back to the default embedded in the bundle.
+ // well, if there was no default then it does remove it.
+ reloadJettyContextHandler(filename, osgiContextHomeFolderCanonicalPath);
+ }
+ });
+
+ }
/**
* Receives notification that a service has had a lifecycle change.
@@ -137,7 +147,7 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
{
try
{
- _helper.unregister(ctxtHandler);
+ getWebBundleDeployerHelp(sr).unregister(ctxtHandler);
}
catch (Exception e)
{
@@ -146,15 +156,15 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
}
}
}
- if (ev.getType() == ServiceEvent.UNREGISTERING)
- {
- break;
- }
- else
- {
- // modified, meaning: we reload it. now that we stopped it;
- // we can register it.
- }
+ if (ev.getType() == ServiceEvent.UNREGISTERING)
+ {
+ break;
+ }
+ else
+ {
+ // modified, meaning: we reload it. now that we stopped it;
+ // we can register it.
+ }
case ServiceEvent.REGISTERED:
{
Bundle contributor = sr.getBundle();
@@ -165,7 +175,11 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
// is configured elsewhere.
return;
}
- if (contextHandler instanceof WebAppContext)
+ String contextFilePath = (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
+ if (contextHandler instanceof WebAppContext && contextFilePath == null)
+ //it could be a web-application that will in fact be configured via a context file.
+ //that case is identified by the fact that the contextFilePath is not null
+ //in that case we must use the register context methods.
{
WebAppContext webapp = (WebAppContext)contextHandler;
String contextPath = (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH);
@@ -186,13 +200,23 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
String war = (String)sr.getProperty("war");
try
{
- ContextHandler handler = _helper.registerWebapplication(contributor,war,contextPath,(String)sr
+ IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr);
+ if (deployerHelper == null)
+ {
+
+ }
+ else
+ {
+ WebAppContext handler = deployerHelper
+ .registerWebapplication(contributor,war,contextPath,(String)sr
.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH),(String)sr
- .getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE),webXmlPath,defaultWebXmlPath);
- if (handler != null)
- {
- registerInIndex(handler,sr);
- }
+ .getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE),
+ webXmlPath,defaultWebXmlPath,webapp);
+ if (handler != null)
+ {
+ registerInIndex(handler,sr);
+ }
+ }
}
catch (Throwable e)
{
@@ -202,20 +226,33 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
else
{
// consider this just an empty skeleton:
- String contextFilePath = (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
if (contextFilePath == null)
{
throw new IllegalArgumentException("the property contextFilePath is required");
}
try
{
- ContextHandler handler = _helper.registerContext(contributor,contextFilePath,(String)sr
- .getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH),(String)sr
- .getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE));
- if (handler != null)
- {
- registerInIndex(handler,sr);
- }
+ IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr);
+ if (deployerHelper == null)
+ {
+ //more warnings?
+ }
+ else
+ {
+ if (Boolean.TRUE.toString().equals(sr.getProperty(
+ IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE)))
+ {
+ contextHandler = null;
+ }
+ ContextHandler handler = deployerHelper.registerContext(contributor,contextFilePath,
+ (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH),
+ (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE),
+ contextHandler);
+ if (handler != null)
+ {
+ registerInIndex(handler,sr);
+ }
+ }
}
catch (Throwable e)
{
@@ -279,9 +316,9 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
*
* @param contextFileFully
*/
- void reloadJettyContextHandler(String canonicalNameOfFileChanged)
+ public void reloadJettyContextHandler(String canonicalNameOfFileChanged, String osgiContextHomeFolderCanonicalPath)
{
- String key = getNormalizedRelativePath(canonicalNameOfFileChanged);
+ String key = getNormalizedRelativePath(canonicalNameOfFileChanged, osgiContextHomeFolderCanonicalPath);
if (key == null)
{
return;
@@ -299,16 +336,45 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
* @param canFilename
* @return
*/
- private String getNormalizedRelativePath(String canFilename)
+ private String getNormalizedRelativePath(String canFilename, String osgiContextHomeFolderCanonicalPath)
{
- if (!canFilename.startsWith(_osgiContextHomeFolderCanonicalPath))
+ if (!canFilename.startsWith(osgiContextHomeFolderCanonicalPath))
{
// why are we here: this does not look like a child of the osgi
// contexts home.
// warning?
return null;
}
- return canFilename.substring(_osgiContextHomeFolderCanonicalPath.length()).replace('\\','/');
+ return canFilename.substring(osgiContextHomeFolderCanonicalPath.length()).replace('\\','/');
}
-
+
+ /**
+ * @return The server on which this webapp is meant to be deployed
+ */
+ private ServerInstanceWrapper getServerInstanceWrapper(String managedServerName)
+ {
+ if (_registry == null)
+ {
+ return null;
+ }
+ if (managedServerName == null)
+ {
+ managedServerName = OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME;
+ }
+ ServerInstanceWrapper wrapper = _registry.getServerInstanceWrapper(managedServerName);
+ System.err.println("Returning " + managedServerName + " = " + wrapper);
+ return wrapper;
+ }
+
+ private IWebBundleDeployerHelper getWebBundleDeployerHelp(ServiceReference sr)
+ {
+ if (_registry == null)
+ {
+ return null;
+ }
+ String managedServerName = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
+ ServerInstanceWrapper wrapper = getServerInstanceWrapper(managedServerName);
+ return wrapper != null ? wrapper.getWebBundleDeployerHelp() : null;
+ }
+
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyHomeHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyHomeHelper.java
deleted file mode 100644
index c6f7c3db49..0000000000
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyHomeHelper.java
+++ /dev/null
@@ -1,272 +0,0 @@
-// ========================================================================
-// 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.webapp;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.Enumeration;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-import org.eclipse.jetty.util.URIUtil;
-
-/**
- * <p>
- * Magically extract the jettyhome folder from this bundle's jar place it
- * somewhere in the file-system. Currently we do this only when we detect a
- * system property 'jetty.magic.home.parent' or if we are inside the pde in dev
- * mode. In dev mode we use the osgi.configuration.area folder.
- * </p>
- * <p>
- * This work is done through the jetty launch configuration inside the
- * "Jetty Config" tab. We could choose to remove this code at some point
- * although it does not hurt and it helps for users who don't go through the
- * jetty launch.
- * </p>
- */
-class JettyHomeHelper
-{
-
- /** only magically extract jettyhome if we are inside the pde. */
- static boolean magic_install_only_in_pde = Boolean.valueOf(System.getProperty("jetty.magic.home.pde.only","true"));
-
- /**
- * Hack for eclipse-PDE. When no jetty.home was set, detect if we running
- * inside eclipse-PDE in development mode. In that case extract the
- * jettyhome folder embedded inside this plugin inside the configuration
- * area folder. It is specific to the workspace. Set the folder as
- * jetty.home. If the folder already exist don't extract it again.
- * <p>
- * If we are not pde dev mode, the same but look in the installation folder
- * of eclipse itself.
- * </p>
- *
- * @return
- * @throws URISyntaxException
- */
- static String setupJettyHomeInEclipsePDE(File thisbundlejar)
- {
- File ecFolder = getParentFolderOfMagicHome();
- if (ecFolder == null || !ecFolder.exists())
- {
- return null;
- }
- File jettyhome = new File(ecFolder,"jettyhome");
- String path;
- try
- {
- path = jettyhome.getCanonicalPath();
- if (jettyhome.exists())
- {
- System.setProperty("jetty.home",path);
- return path;
- }
- else
- {
- // now grab the jar and unzip the relevant portion
- unzipJettyHomeIntoDirectory(thisbundlejar,ecFolder);
- System.setProperty("jetty.home",path);
- return path;
- }
- }
- catch (IOException e)
- {
- e.printStackTrace();
- }
- return null;
- }
-
- /**
- * @return true when we are currently being run by the pde in development
- * mode.
- */
- private static boolean isPDEDevelopment()
- {
- String eclipseCommands = System.getProperty("eclipse.commands");
- // detect if we are being run from the pde: ie during development.
- return eclipseCommands != null && eclipseCommands.indexOf("-dev") != -1
- && (eclipseCommands.indexOf("-dev\n") != -1 || eclipseCommands.indexOf("-dev\r") != -1 || eclipseCommands.indexOf("-dev ") != -1);
- }
-
- /**
- * @return
- */
- private static File getConfigurationAreaDirectory()
- {
- return getFile(System.getProperty("osgi.configuration.area"));
- }
-
- /**
- * @param zipFile
- * The current jar file for this bundle. contains an archive of
- * the default jettyhome
- * @param parentOfMagicJettyHome
- * The folder inside which jettyhome is created.
- */
- private static void unzipJettyHomeIntoDirectory(File thisbundlejar, File parentOfMagicJettyHome) throws IOException
- {
- ZipFile zipFile = null;
- try
- {
- zipFile = new ZipFile(thisbundlejar);
- Enumeration<? extends ZipEntry> files = zipFile.entries();
- File f = null;
- FileOutputStream fos = null;
-
- while (files.hasMoreElements())
- {
- try
- {
- ZipEntry entry = files.nextElement();
- String entryName = entry.getName();
- if (!entryName.startsWith("jettyhome"))
- {
- continue;
- }
-
- InputStream eis = zipFile.getInputStream(entry);
- byte[] buffer = new byte[1024];
- int bytesRead = 0;
- f = new File(parentOfMagicJettyHome,entry.getName());
-
- if (entry.isDirectory())
- {
- f.mkdirs();
- }
- else
- {
- f.getParentFile().mkdirs();
- f.createNewFile();
- fos = new FileOutputStream(f);
- while ((bytesRead = eis.read(buffer)) != -1)
- {
- fos.write(buffer,0,bytesRead);
- }
- }
- }
- catch (IOException e)
- {
- e.printStackTrace();
- continue;
- }
- finally
- {
- if (fos != null)
- {
- try
- {
- fos.close();
- }
- catch (IOException e)
- {
- }
- fos = null;
- }
- }
- }
- }
- finally
- {
- if (zipFile != null)
- try
- {
- zipFile.close();
- }
- catch (Throwable t)
- {
- }
- }
- }
-
- /**
- * Look for the parent folder that contains jettyhome. Can be specified by
- * the sys property jetty.magic.home.parent or if inside the pde will
- * default on the configuration area. Otherwise returns null.
- *
- * @return The folder inside which jettyhome should be placed.
- */
- private static File getParentFolderOfMagicHome()
- {
- // for (java.util.Map.Entry<Object, Object> e :
- // System.getProperties().entrySet()) {
- // System.err.println(e.getKey() + " -> " + e.getValue());
- // }
- String magicParent = WebappRegistrationHelper.stripQuotesIfPresent(System.getProperty("jetty.magic.home.parent"));
- String magicParentValue = magicParent != null?System.getProperty(magicParent):null;
- File specifiedMagicParent = magicParentValue != null?getFile(magicParentValue) // in
- // that
- // case
- // it
- // was
- // pointing
- // to
- // another
- // system
- // property.
- :getFile(magicParent); // in that case it was directly a file.
- if (specifiedMagicParent != null && specifiedMagicParent.exists())
- {
- return specifiedMagicParent;
- }
- if (isPDEDevelopment())
- {
- return getConfigurationAreaDirectory();
- }
- return null;
- }
-
- /**
- * Be flexible with the url/uri/path that can be the value of the various
- * system properties.
- *
- * @param file
- * @return a file. might not exist.
- */
- private static File getFile(String file)
- {
- if (file == null)
- {
- return null;
- }
- file = WebappRegistrationHelper.stripQuotesIfPresent(file);
- try
- {
- if (file.startsWith("file:/"))
- {
- if (!file.startsWith("file://"))
- {
- return new File(new URI(URIUtil.encodePath(file)));
- }
- else
- {
- return new File(new URL(file).toURI());
- }
- }
- else
- {
- return new File(file);
- }
- }
- catch (Throwable t)
- {
- t.printStackTrace();
- return new File(file);
- }
-
- }
-}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java
index e93585277d..ee5e3c97de 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java
@@ -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;
@@ -91,9 +92,13 @@ public class LibExtClassLoaderHelper
* is the JettyBootStrapper (an osgi classloader.
* @throws MalformedURLException
*/
- public static URLClassLoader createLibEtcClassLoaderHelper(File jettyHome, Server server, ClassLoader parentClassLoader) throws MalformedURLException
+ public static ClassLoader createLibEtcClassLoader(File jettyHome, Server server,
+ ClassLoader parentClassLoader) throws MalformedURLException
{
-
+ if (jettyHome == null)
+ {
+ return parentClassLoader;
+ }
ArrayList<URL> urls = new ArrayList<URL>();
File jettyResources = new File(jettyHome,"resources");
if (jettyResources.exists())
@@ -139,6 +144,52 @@ public class LibExtClassLoaderHelper
}
/**
+ * @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).
+ * If there was no extra jars to insert, then just return the parentClassLoader.
+ * @throws MalformedURLException
+ */
+ public static ClassLoader createLibExtClassLoader(List<File> jarsContainerOrJars,
+ List<URL> otherJarsOrFolder, Server server,
+ ClassLoader parentClassLoader) throws MalformedURLException
+ {
+ if (jarsContainerOrJars == null && otherJarsOrFolder == null)
+ {
+ return parentClassLoader;
+ }
+ List<URL> urls = new ArrayList<URL>();
+ if (otherJarsOrFolder != null)
+ {
+ urls.addAll(otherJarsOrFolder);
+ }
+ if (jarsContainerOrJars != null)
+ {
+ 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
* depending too much directly on a particular logging framework.
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java
index 50cec93fda..13c097e85e 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java
@@ -29,18 +29,20 @@ import java.util.jar.JarFile;
import javax.servlet.http.HttpServlet;
+import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelper;
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.webapp.WebAppClassLoader;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
/**
* Extends the webappclassloader to insert the classloader provided by the osgi
* bundle at the same level than any other jars palced in the webappclassloader.
*/
-public class OSGiWebappClassLoader extends WebAppClassLoader
+public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleReference
{
private Logger __logger = Log.getLogger(OSGiWebappClassLoader.class.getName().toString());
@@ -67,14 +69,34 @@ public class OSGiWebappClassLoader extends WebAppClassLoader
}
private ClassLoader _osgiBundleClassLoader;
+ private Bundle _contributor;
private boolean _lookInOsgiFirst = true;
private Set<String> _libsAlreadyInManifest = new HashSet<String>();
- public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor) throws IOException
+ /**
+ * @param parent The parent classloader. In this case
+ * @param context The WebAppContext
+ * @param contributor The bundle that defines this web-application.
+ * @throws IOException
+ */
+ public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor,
+ BundleClassLoaderHelper bundleClassLoaderHelper) throws IOException
{
super(parent,context);
- _osgiBundleClassLoader = WebappRegistrationHelper.BUNDLE_CLASS_LOADER_HELPER.getBundleClassLoader(contributor);
+ _contributor = contributor;
+ _osgiBundleClassLoader = bundleClassLoaderHelper.getBundleClassLoader(contributor);
}
+
+ /**
+ * Returns the <code>Bundle</code> that defined this web-application.
+ *
+ * @return The <code>Bundle</code> object associated with this
+ * <code>BundleReference</code>.
+ */
+ public Bundle getBundle()
+ {
+ return _contributor;
+ }
/**
* Reads the manifest. If the manifest is already configured to loads a few
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java
new file mode 100644
index 0000000000..f43dbb31df
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java
@@ -0,0 +1,619 @@
+// ========================================================================
+// 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.
+// Contributors:
+// Hugues Malphettes - initial API and implementation
+// ========================================================================
+package org.eclipse.jetty.osgi.boot.internal.webapp;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+
+import org.eclipse.jetty.deploy.ContextDeployer;
+import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
+import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelper;
+import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
+import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
+import org.eclipse.jetty.osgi.boot.utils.internal.DefaultBundleClassLoaderHelper;
+import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
+import org.eclipse.jetty.server.handler.ContextHandler;
+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.webapp.WebAppContext;
+import org.eclipse.jetty.xml.XmlConfiguration;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.xml.sax.SAXException;
+
+/**
+ * Bridges the jetty deployers with the OSGi lifecycle where applications are
+ * managed inside OSGi-bundles.
+ * <p>
+ * This class should be called as a consequence of the activation of a new
+ * service that is a ContextHandler.<br/>
+ * This way the new webapps are exposed as OSGi services.
+ * </p>
+ * <p>
+ * Helper methods to register a bundle that is a web-application or a context.
+ * </p>
+ * Limitations:
+ * <ul>
+ * <li>support for jarred webapps is somewhat limited.</li>
+ * </ul>
+ */
+public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
+{
+
+ private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName());
+
+ private static boolean INITIALIZED = false;
+
+ /**
+ * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
+ * equinox and apache-felix fragment bundles that are specific to an OSGi
+ * implementation should set a different implementation.
+ */
+ public static BundleClassLoaderHelper BUNDLE_CLASS_LOADER_HELPER = null;
+ /**
+ * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
+ * equinox and apache-felix fragment bundles that are specific to an OSGi
+ * implementation should set a different implementation.
+ */
+ public static BundleFileLocatorHelper BUNDLE_FILE_LOCATOR_HELPER = null;
+
+ /**
+ * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
+ * equinox and apache-felix fragment bundles that are specific to an OSGi
+ * implementation should set a different implementation.
+ * <p>
+ * Several of those objects can be added here: For example we could have an optional fragment that setups
+ * a specific implementation of JSF for the whole of jetty-osgi.
+ * </p>
+ */
+ public static Collection<WebappRegistrationCustomizer> JSP_REGISTRATION_HELPERS = new ArrayList<WebappRegistrationCustomizer>();
+
+ /**
+ * this class loader loads the jars inside {$jetty.home}/lib/ext it is meant
+ * as a migration path and for jars that are not OSGi ready. also gives
+ * access to the jsp jars.
+ */
+ // private URLClassLoader _libExtClassLoader;
+
+ private ServerInstanceWrapper _wrapper;
+
+ public WebBundleDeployerHelper(ServerInstanceWrapper wrapper)
+ {
+ staticInit();
+ _wrapper = wrapper;
+ }
+
+ // Inject the customizing classes that might be defined in fragment bundles.
+ public static synchronized void staticInit()
+ {
+ if (!INITIALIZED)
+ {
+ INITIALIZED = true;
+ // setup the custom BundleClassLoaderHelper
+ try
+ {
+ BUNDLE_CLASS_LOADER_HELPER = (BundleClassLoaderHelper)Class.forName(BundleClassLoaderHelper.CLASS_NAME).newInstance();
+ }
+ catch (Throwable t)
+ {
+ // System.err.println("support for equinox and felix");
+ BUNDLE_CLASS_LOADER_HELPER = new DefaultBundleClassLoaderHelper();
+ }
+ // setup the custom FileLocatorHelper
+ try
+ {
+ BUNDLE_FILE_LOCATOR_HELPER = (BundleFileLocatorHelper)Class.forName(BundleFileLocatorHelper.CLASS_NAME).newInstance();
+ }
+ catch (Throwable t)
+ {
+ // System.err.println("no jsp/jasper support");
+ BUNDLE_FILE_LOCATOR_HELPER = new DefaultFileLocatorHelper();
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#registerWebapplication(org.osgi.framework.Bundle, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ public WebAppContext registerWebapplication(Bundle bundle, String webappFolderPath, String contextPath, String extraClasspath,
+ String overrideBundleInstallLocation, String webXmlPath, String defaultWebXmlPath, WebAppContext webAppContext) throws Exception
+ {
+ File bundleInstall = overrideBundleInstallLocation == null?BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(bundle):new File(
+ overrideBundleInstallLocation);
+ File webapp = null;
+ URL baseWebappInstallURL = null;
+ if (webappFolderPath != null && webappFolderPath.length() != 0 && !webappFolderPath.equals("."))
+ {
+ if (webappFolderPath.startsWith("/") || webappFolderPath.startsWith("file:"))
+ {
+ webapp = new File(webappFolderPath);
+ }
+ else if (bundleInstall != null && bundleInstall.isDirectory())
+ {
+ webapp = new File(bundleInstall,webappFolderPath);
+ }
+ else if (bundleInstall != null)
+ {
+ Enumeration<URL> urls = BUNDLE_FILE_LOCATOR_HELPER.findEntries(bundle, webappFolderPath);
+ if (urls != null && urls.hasMoreElements())
+ {
+ baseWebappInstallURL = urls.nextElement();
+ }
+ }
+ }
+ else
+ {
+ webapp = bundleInstall;
+ }
+ if (baseWebappInstallURL == null && (webapp == null || !webapp.exists()))
+ {
+ throw new IllegalArgumentException("Unable to locate " + webappFolderPath + " inside "
+ + (bundleInstall != null?bundleInstall.getAbsolutePath():"unlocated bundle '" + bundle.getSymbolicName() + "'"));
+ }
+ if (baseWebappInstallURL == null && webapp != null)
+ {
+ baseWebappInstallURL = webapp.toURI().toURL();
+ }
+ return registerWebapplication(bundle,webappFolderPath,baseWebappInstallURL,contextPath,
+ extraClasspath,bundleInstall,webXmlPath,defaultWebXmlPath,webAppContext);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#registerWebapplication(org.osgi.framework.Bundle, java.lang.String, java.io.File, java.lang.String, java.lang.String, java.io.File, java.lang.String, java.lang.String)
+ */
+ private WebAppContext registerWebapplication(Bundle contributor, String pathInBundleToWebApp,
+ URL baseWebappInstallURL, String contextPath, String extraClasspath, File bundleInstall,
+ String webXmlPath, String defaultWebXmlPath, WebAppContext context) throws Exception
+ {
+
+ ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
+ String[] oldServerClasses = null;
+
+ try
+ {
+ // make sure we provide access to all the jetty bundles by going
+ // through this bundle.
+ OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
+ // configure with access to all jetty classes and also all the classes
+ // that the contributor gives access to.
+ Thread.currentThread().setContextClassLoader(composite);
+
+ context.setWar(baseWebappInstallURL.toString());
+ context.setContextPath(contextPath);
+ context.setExtraClasspath(extraClasspath);
+
+ if (webXmlPath != null && webXmlPath.length() != 0)
+ {
+ File webXml = null;
+ if (webXmlPath.startsWith("/") || webXmlPath.startsWith("file:/"))
+ {
+ webXml = new File(webXmlPath);
+ }
+ else
+ {
+ webXml = new File(bundleInstall,webXmlPath);
+ }
+ if (webXml.exists())
+ {
+ context.setDescriptor(webXml.getAbsolutePath());
+ }
+ }
+
+ if (defaultWebXmlPath == null || defaultWebXmlPath.length() == 0)
+ {
+ //use the one defined by the OSGiAppProvider.
+ defaultWebXmlPath = _wrapper.getOSGiAppProvider().getDefaultsDescriptor();
+ }
+ if (defaultWebXmlPath != null && defaultWebXmlPath.length() != 0)
+ {
+ File defaultWebXml = null;
+ if (defaultWebXmlPath.startsWith("/") || defaultWebXmlPath.startsWith("file:/"))
+ {
+ defaultWebXml = new File(webXmlPath);
+ }
+ else
+ {
+ defaultWebXml = new File(bundleInstall,defaultWebXmlPath);
+ }
+ if (defaultWebXml.exists())
+ {
+ context.setDefaultsDescriptor(defaultWebXml.getAbsolutePath());
+ }
+ }
+
+ //other parameters that might be defines on the OSGiAppProvider:
+ context.setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority());
+
+ configureWebAppContext(context,contributor);
+ configureWebappClassLoader(contributor,context,composite);
+
+ // @see
+ // org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext)
+ // during initialization of the webapp all the jetty packages are
+ // visible
+ // through the webapp classloader.
+ oldServerClasses = context.getServerClasses();
+ context.setServerClasses(null);
+
+ _wrapper.getOSGiAppProvider().addContext(contributor,pathInBundleToWebApp,context);
+
+ return context;
+ }
+ finally
+ {
+ if (context != null && oldServerClasses != null)
+ {
+ context.setServerClasses(oldServerClasses);
+ }
+ Thread.currentThread().setContextClassLoader(contextCl);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#unregister(org.eclipse.jetty.server.handler.ContextHandler)
+ */
+ public void unregister(ContextHandler contextHandler) throws Exception
+ {
+ _wrapper.getOSGiAppProvider().removeContext(contextHandler);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#registerContext(org.osgi.framework.Bundle, java.lang.String, java.lang.String, java.lang.String)
+ */
+ public ContextHandler registerContext(Bundle contributor, String contextFileRelativePath, String extraClasspath,
+ String overrideBundleInstallLocation, ContextHandler handler)
+ throws Exception
+ {
+ File contextsHome = _wrapper.getOSGiAppProvider().getContextXmlDirAsFile();
+ if (contextsHome != null)
+ {
+ File prodContextFile = new File(contextsHome,contributor.getSymbolicName() + "/" + contextFileRelativePath);
+ if (prodContextFile.exists())
+ {
+ return registerContext(contributor,contextFileRelativePath,prodContextFile,extraClasspath,
+ overrideBundleInstallLocation,handler);
+ }
+ }
+ File rootFolder = overrideBundleInstallLocation != null
+ ? Resource.newResource(overrideBundleInstallLocation).getFile()
+ : BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(contributor);
+ File contextFile = rootFolder != null?new File(rootFolder,contextFileRelativePath):null;
+ if (contextFile != null && contextFile.exists())
+ {
+ return registerContext(contributor,contextFileRelativePath,contextFile,extraClasspath,overrideBundleInstallLocation,handler);
+ }
+ else
+ {
+ if (contextFileRelativePath.startsWith("./"))
+ {
+ contextFileRelativePath = contextFileRelativePath.substring(1);
+ }
+ if (!contextFileRelativePath.startsWith("/"))
+ {
+ contextFileRelativePath = "/" + contextFileRelativePath;
+ }
+
+ URL contextURL = contributor.getEntry(contextFileRelativePath);
+ if (contextURL != null)
+ {
+ return registerContext(contributor,contextFileRelativePath,contextURL.openStream(),extraClasspath,overrideBundleInstallLocation,handler);
+ }
+ throw new IllegalArgumentException("Could not find the context " + "file " + contextFileRelativePath + " for the bundle "
+ + contributor.getSymbolicName() + (overrideBundleInstallLocation != null?" using the install location " + overrideBundleInstallLocation:""));
+ }
+ }
+
+ /**
+ * This type of registration relies on jetty's complete context xml file.
+ * Context encompasses jndi and all other things. This makes the definition
+ * of the webapp a lot more self-contained.
+ *
+ * @param webapp
+ * @param contextPath
+ * @param classInBundle
+ * @throws Exception
+ */
+ private ContextHandler registerContext(Bundle contributor, String pathInBundle, File contextFile,
+ String extraClasspath, String overrideBundleInstallLocation, ContextHandler handler) throws Exception
+ {
+ InputStream contextFileInputStream = null;
+ try
+ {
+ contextFileInputStream = new BufferedInputStream(new FileInputStream(contextFile));
+ return registerContext(contributor, pathInBundle, contextFileInputStream,extraClasspath,overrideBundleInstallLocation, handler);
+ }
+ finally
+ {
+ IO.close(contextFileInputStream);
+ }
+ }
+
+ /**
+ * @param contributor
+ * @param contextFileInputStream
+ * @return The ContextHandler created and registered or null if it did not
+ * happen.
+ * @throws Exception
+ */
+ private ContextHandler registerContext(Bundle contributor, String pathInsideBundle, InputStream contextFileInputStream,
+ String extraClasspath, String overrideBundleInstallLocation, ContextHandler handler)
+ throws Exception
+ {
+ ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
+ String[] oldServerClasses = null;
+ WebAppContext webAppContext = null;
+ try
+ {
+ // make sure we provide access to all the jetty bundles by going
+ // through this bundle.
+ OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
+ // configure with access to all jetty classes and also all the
+ // classes
+ // that the contributor gives access to.
+ Thread.currentThread().setContextClassLoader(composite);
+ ContextHandler context = createContextHandler(handler, contributor,contextFileInputStream,extraClasspath,overrideBundleInstallLocation);
+ if (context == null)
+ {
+ return null;// did not happen
+ }
+
+ // ok now register this webapp. we checked when we started jetty
+ // that there
+ // was at least one such handler for webapps.
+ //the actual registration must happen via the new Deployment API.
+// _ctxtHandler.addHandler(context);
+
+ configureWebappClassLoader(contributor,context,composite);
+ if (context instanceof WebAppContext)
+ {
+ webAppContext = (WebAppContext)context;
+ // @see
+ // org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext)
+ oldServerClasses = webAppContext.getServerClasses();
+ webAppContext.setServerClasses(null);
+ }
+ _wrapper.getOSGiAppProvider().addContext(contributor, pathInsideBundle, context);
+ return context;
+ }
+ finally
+ {
+ if (webAppContext != null)
+ {
+ webAppContext.setServerClasses(oldServerClasses);
+ }
+ Thread.currentThread().setContextClassLoader(contextCl);
+ }
+
+ }
+
+ /**
+ * Applies the properties of WebAppDeployer as defined in jetty.xml.
+ *
+ * @see {WebAppDeployer#scan} around the comment
+ * <code>// configure it</code>
+ */
+ protected void configureWebAppContext(WebAppContext wah, Bundle contributor)
+ {
+ // rfc66
+ wah.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT,contributor.getBundleContext());
+
+ //spring-dm-1.2.1 looks for the BundleContext as a different attribute.
+ //not a spec... but if we want to support
+ //org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
+ //then we need to do this to:
+ wah.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(),
+ contributor.getBundleContext());
+
+ }
+
+ /**
+ * @See {@link ContextDeployer#scan}
+ * @param contextFile
+ * @return
+ */
+ protected ContextHandler createContextHandler(ContextHandler handlerToConfigure,
+ Bundle bundle, File contextFile, String extraClasspath, String overrideBundleInstallLocation)
+ {
+ try
+ {
+ return createContextHandler(handlerToConfigure,bundle,new BufferedInputStream(new FileInputStream(contextFile)),extraClasspath,overrideBundleInstallLocation);
+ }
+ catch (FileNotFoundException e)
+ {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * @See {@link ContextDeployer#scan}
+ * @param contextFile
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ protected ContextHandler createContextHandler(ContextHandler handlerToConfigure,
+ Bundle bundle, InputStream contextInputStream, String extraClasspath, String overrideBundleInstallLocation)
+ {
+ /*
+ * Do something identical to what the ContextProvider would have done:
+ * XmlConfiguration xmlConfiguration=new
+ * XmlConfiguration(resource.getURL()); HashMap properties = new
+ * HashMap(); properties.put("Server", _contexts.getServer()); if
+ * (_configMgr!=null) properties.putAll(_configMgr.getProperties());
+ *
+ * xmlConfiguration.setProperties(properties); ContextHandler
+ * context=(ContextHandler)xmlConfiguration.configure();
+ * context.setAttributes(new AttributesMap(_contextAttributes));
+ */
+ try
+ {
+ XmlConfiguration xmlConfiguration = new XmlConfiguration(contextInputStream);
+ HashMap properties = new HashMap();
+ properties.put("Server",_wrapper.getServer());
+
+ // insert the bundle's location as a property.
+ setThisBundleHomeProperty(bundle,properties,overrideBundleInstallLocation);
+ xmlConfiguration.setProperties(properties);
+
+ ContextHandler context = null;
+ if (handlerToConfigure == null)
+ {
+ context = (ContextHandler)xmlConfiguration.configure();
+ }
+ else
+ {
+ xmlConfiguration.configure(handlerToConfigure);
+ context = handlerToConfigure;
+ }
+
+ if (context instanceof WebAppContext)
+ {
+ ((WebAppContext)context).setExtraClasspath(extraClasspath);
+ ((WebAppContext)context).setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority());
+ if (_wrapper.getOSGiAppProvider().getDefaultsDescriptor() != null && _wrapper.getOSGiAppProvider().getDefaultsDescriptor().length() != 0)
+ {
+ ((WebAppContext)context).setDefaultsDescriptor(_wrapper.getOSGiAppProvider().getDefaultsDescriptor());
+ }
+ }
+
+ // rfc-66:
+ context.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT,bundle.getBundleContext());
+
+ //spring-dm-1.2.1 looks for the BundleContext as a different attribute.
+ //not a spec... but if we want to support
+ //org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
+ //then we need to do this to:
+ context.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(),
+ bundle.getBundleContext());
+ return context;
+ }
+ catch (FileNotFoundException e)
+ {
+ return null;
+ }
+ catch (SAXException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ catch (IOException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ catch (Throwable e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ finally
+ {
+ IO.close(contextInputStream);
+ }
+ return null;
+ }
+
+ /**
+ * Configure a classloader onto the context. If the context is a
+ * WebAppContext, build a WebAppClassLoader that has access to all the jetty
+ * classes thanks to the classloader of the JettyBootStrapper bundle and
+ * also has access to the classloader of the bundle that defines this
+ * context.
+ * <p>
+ * If the context is not a WebAppContext, same but with a simpler
+ * URLClassLoader. Note that the URLClassLoader is pretty much fake: it
+ * delegate all actual classloading to the parent classloaders.
+ * </p>
+ * <p>
+ * The URL[] returned by the URLClassLoader create contained specifically
+ * the jars that some j2ee tools expect and look into. For example the jars
+ * that contain tld files for jasper's jstl support.
+ * </p>
+ * <p>
+ * Also as the jars in the lib folder and the classes in the classes folder
+ * might already be in the OSGi classloader we filter them out of the
+ * WebAppClassLoader
+ * </p>
+ *
+ * @param context
+ * @param contributor
+ * @param webapp
+ * @param contextPath
+ * @param classInBundle
+ * @throws Exception
+ */
+ protected void configureWebappClassLoader(Bundle contributor, ContextHandler context, OSGiWebappClassLoader webappClassLoader) throws Exception
+ {
+ if (context instanceof WebAppContext)
+ {
+ WebAppContext webappCtxt = (WebAppContext)context;
+ context.setClassLoader(webappClassLoader);
+ webappClassLoader.setWebappContext(webappCtxt);
+ }
+ else
+ {
+ context.setClassLoader(webappClassLoader);
+ }
+ }
+
+ /**
+ * No matter what the type of webapp, we create a WebappClassLoader.
+ */
+ protected OSGiWebappClassLoader createWebappClassLoader(Bundle contributor) throws Exception
+ {
+ // we use a temporary WebAppContext object.
+ // if this is a real webapp we will set it on it a bit later: once we
+ // know.
+ OSGiWebappClassLoader webappClassLoader = new OSGiWebappClassLoader(
+ _wrapper.getParentClassLoaderForWebapps(),new WebAppContext(),contributor,BUNDLE_CLASS_LOADER_HELPER);
+ return webappClassLoader;
+ }
+
+ /**
+ * Set the property &quot;this.bundle.install&quot; to point to the location
+ * of the bundle. Useful when <SystemProperty name="this.bundle.home"/> is
+ * used.
+ */
+ private void setThisBundleHomeProperty(Bundle bundle, HashMap<String, Object> properties, String overrideBundleInstallLocation)
+ {
+ try
+ {
+ File location = overrideBundleInstallLocation != null?new File(overrideBundleInstallLocation):BUNDLE_FILE_LOCATOR_HELPER
+ .getBundleInstallLocation(bundle);
+ properties.put("this.bundle.install",location.getCanonicalPath());
+ properties.put("this.bundle.install.url",bundle.getEntry("/").toString());
+ }
+ catch (Throwable t)
+ {
+ System.err.println("Unable to set 'this.bundle.install' " + " for the bundle " + bundle.getSymbolicName());
+ t.printStackTrace();
+ }
+ }
+
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerExtender.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java
index b82bd29050..df257839fd 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerExtender.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java
@@ -1,5 +1,5 @@
// ========================================================================
-// Copyright (c) 2009 Intalio, Inc.
+// Copyright (c) 2009-2010 Intalio, Inc.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
@@ -18,9 +18,10 @@ import java.util.Dictionary;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
-import org.osgi.framework.BundleListener;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+
/**
* Support bundles that declare the webapp directly through headers in their
@@ -46,44 +47,102 @@ import org.osgi.framework.BundleListener;
*
* @author hmalphettes
*/
-public class JettyContextHandlerExtender implements BundleListener
-{
+public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer {
+
- /**
- * Receives notification that a bundle has had a lifecycle change.
- *
- * @param event
- * The <code>BundleEvent</code>.
- */
- public void bundleChanged(BundleEvent event)
- {
- switch (event.getType())
- {
- case BundleEvent.STARTED:
- register(event.getBundle());
- break;
- case BundleEvent.STOPPING:
- unregister(event.getBundle());
- break;
- }
- }
+ /**
+ * A bundle is being added to the <code>BundleTracker</code>.
+ *
+ * <p>
+ * This method is called before a bundle which matched the search parameters
+ * of the <code>BundleTracker</code> is added to the
+ * <code>BundleTracker</code>. This method should return the object to be
+ * tracked for the specified <code>Bundle</code>. The returned object is
+ * stored in the <code>BundleTracker</code> and is available from the
+ * {@link BundleTracker#getObject(Bundle) getObject} method.
+ *
+ * @param bundle The <code>Bundle</code> being added to the
+ * <code>BundleTracker</code>.
+ * @param event The bundle event which caused this customizer method to be
+ * called or <code>null</code> if there is no bundle event associated
+ * with the call to this method.
+ * @return The object to be tracked for the specified <code>Bundle</code>
+ * object or <code>null</code> if the specified <code>Bundle</code>
+ * object should not be tracked.
+ */
+ public Object addingBundle(Bundle bundle, BundleEvent event)
+ {
+ if (bundle.getState() == Bundle.ACTIVE)
+ {
+ boolean isWebBundle = register(bundle);
+ return isWebBundle ? bundle : null;
+ }
+ else if (bundle.getState() == Bundle.STOPPING)
+ {
+ unregister(bundle);
+ }
+ else
+ {
+ //we should not be called in that state as
+ //we are registered only for ACTIVE and STOPPING
+ }
+ return null;
+ }
- /**
+ /**
+ * A bundle tracked by the <code>BundleTracker</code> has been modified.
*
+ * <p>
+ * This method is called when a bundle being tracked by the
+ * <code>BundleTracker</code> has had its state modified.
+ *
+ * @param bundle The <code>Bundle</code> whose state has been modified.
+ * @param event The bundle event which caused this customizer method to be
+ * called or <code>null</code> if there is no bundle event associated
+ * with the call to this method.
+ * @param object The tracked object for the specified bundle.
*/
- public void init(BundleContext context)
- {
- Bundle bundles[] = context.getBundles();
- for (int i = 0; i < bundles.length; i++)
- {
- if ((bundles[i].getState() & (Bundle.STARTING | Bundle.ACTIVE)) != 0)
- {
- register(bundles[i]);
- }
- }
- }
+ public void modifiedBundle(Bundle bundle, BundleEvent event,
+ Object object)
+ {
+ //nothing the web-bundle was already track. something changed.
+ //we only reload the webapps if the bundle is stopped and restarted.
+// System.err.println(bundle.getSymbolicName());
+ if (bundle.getState() == Bundle.STOPPING || bundle.getState() == Bundle.ACTIVE)
+ {
+ unregister(bundle);
+ }
+ if (bundle.getState() == Bundle.ACTIVE)
+ {
+ register(bundle);
+ }
+ }
- private void register(Bundle bundle)
+ /**
+ * A bundle tracked by the <code>BundleTracker</code> has been removed.
+ *
+ * <p>
+ * This method is called after a bundle is no longer being tracked by the
+ * <code>BundleTracker</code>.
+ *
+ * @param bundle The <code>Bundle</code> that has been removed.
+ * @param event The bundle event which caused this customizer method to be
+ * called or <code>null</code> if there is no bundle event associated
+ * with the call to this method.
+ * @param object The tracked object for the specified bundle.
+ */
+ public void removedBundle(Bundle bundle, BundleEvent event,
+ Object object)
+ {
+ unregister(bundle);
+ }
+
+
+ /**
+ * @param bundle
+ * @return true if this bundle in indeed a web-bundle.
+ */
+ private boolean register(Bundle bundle)
{
Dictionary<?, ?> dic = bundle.getHeaders();
String warFolderRelativePath = (String)dic.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
@@ -99,11 +158,13 @@ public class JettyContextHandlerExtender implements BundleListener
try
{
JettyBootstrapActivator.registerWebapplication(bundle,warFolderRelativePath,contextPath);
+ return true;
}
catch (Throwable e)
{
// TODO Auto-generated catch block
e.printStackTrace();
+ return true;//maybe it did not work maybe it did. safer to track this bundle.
}
}
else if (dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH) != null)
@@ -112,7 +173,7 @@ public class JettyContextHandlerExtender implements BundleListener
if (contextFileRelativePath == null)
{
// nothing to register here.
- return;
+ return false;
}
// support for multiple webapps in the same bundle:
String[] pathes = contextFileRelativePath.split(",;");
@@ -128,6 +189,7 @@ public class JettyContextHandlerExtender implements BundleListener
e.printStackTrace();
}
}
+ return true;
}
else
{
@@ -137,7 +199,7 @@ public class JettyContextHandlerExtender implements BundleListener
URL rfc66Webxml = bundle.getEntry("/WEB-INF/web.xml");
if (rfc66Webxml == null)
{
- return;// no webapp in here
+ return false;// no webapp in here
}
// this is risky: should we make sure that there is no classes and
// jars directly available
@@ -151,11 +213,13 @@ public class JettyContextHandlerExtender implements BundleListener
try
{
JettyBootstrapActivator.registerWebapplication(bundle,".",rfc66ContextPath);
+ return true;
}
catch (Throwable e)
{
// TODO Auto-generated catch block
e.printStackTrace();
+ return true;//maybe it did not work maybe it did. safer to track this bundle.
}
}
}
@@ -195,4 +259,7 @@ public class JettyContextHandlerExtender implements BundleListener
// webapps registered in that bundle.
}
+
+
+
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebappRegistrationHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebappRegistrationHelper.java
deleted file mode 100644
index 447893d315..0000000000
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebappRegistrationHelper.java
+++ /dev/null
@@ -1,979 +0,0 @@
-// ========================================================================
-// 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.
-// Contributors:
-// Hugues Malphettes - initial API and implementation
-// ========================================================================
-package org.eclipse.jetty.osgi.boot.internal.webapp;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-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.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-import java.util.jar.JarFile;
-import java.util.zip.ZipEntry;
-
-import org.eclipse.jetty.deploy.AppProvider;
-import org.eclipse.jetty.deploy.ContextDeployer;
-import org.eclipse.jetty.deploy.DeploymentManager;
-import org.eclipse.jetty.deploy.WebAppDeployer;
-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.utils.BundleClassLoaderHelper;
-import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
-import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
-import org.eclipse.jetty.osgi.boot.utils.internal.DefaultBundleClassLoaderHelper;
-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.ContextHandler;
-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.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.resource.Resource;
-import org.eclipse.jetty.webapp.WebAppContext;
-import org.eclipse.jetty.xml.XmlConfiguration;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
-/**
- * Bridges the jetty deployers with the OSGi lifecycle where applications are
- * managed inside OSGi-bundles.
- * <p>
- * This class should be called as a consequence of the activation of a new
- * service that is a ContextHandler.<br/>
- * This way the new webapps are exposed as OSGi services.
- * </p>
- * <p>
- * Helper methods to register a bundle that is a web-application or a context.
- * </p>
- * Limitations:
- * <ul>
- * <li>support for jarred webapps is somewhat limited.</li>
- * </ul>
- */
-public class WebappRegistrationHelper
-{
-
- private static Logger __logger = Log.getLogger(WebappRegistrationHelper.class.getName());
-
- private static boolean INITIALIZED = false;
-
- /**
- * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
- * equinox and apache-felix fragment bundles that are specific to an OSGi
- * implementation should set a different implementation.
- */
- public static BundleClassLoaderHelper BUNDLE_CLASS_LOADER_HELPER = null;
- /**
- * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
- * equinox and apache-felix fragment bundles that are specific to an OSGi
- * implementation should set a different implementation.
- */
- public static BundleFileLocatorHelper BUNDLE_FILE_LOCATOR_HELPER = null;
-
- /**
- * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
- * equinox and apache-felix fragment bundles that are specific to an OSGi
- * implementation should set a different implementation.
- * <p>
- * Several of those objects can be added here: For example we could have an optional fragment that setups
- * a specific implementation of JSF for the whole of jetty-osgi.
- * </p>
- */
- public static Collection<WebappRegistrationCustomizer> JSP_REGISTRATION_HELPERS = new ArrayList<WebappRegistrationCustomizer>();
-
- private Server _server;
- private ContextHandlerCollection _ctxtHandler;
-
- /**
- * this class loader loads the jars inside {$jetty.home}/lib/ext it is meant
- * as a migration path and for jars that are not OSGi ready. also gives
- * access to the jsp jars.
- */
- // private URLClassLoader _libExtClassLoader;
-
- /**
- * 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 URLClassLoader _commonParentClassLoaderForWebapps;
-
- private DeploymentManager _deploymentManager;
-
- private OSGiAppProvider _provider;
-
- public WebappRegistrationHelper(Server server)
- {
- _server = server;
- staticInit();
- }
-
- // Inject the customizing classes that might be defined in fragment bundles.
- private static synchronized void staticInit()
- {
- if (!INITIALIZED)
- {
- INITIALIZED = true;
- // setup the custom BundleClassLoaderHelper
- try
- {
- BUNDLE_CLASS_LOADER_HELPER = (BundleClassLoaderHelper)Class.forName(BundleClassLoaderHelper.CLASS_NAME).newInstance();
- }
- catch (Throwable t)
- {
- // System.err.println("support for equinox and felix");
- BUNDLE_CLASS_LOADER_HELPER = new DefaultBundleClassLoaderHelper();
- }
- // setup the custom FileLocatorHelper
- try
- {
- BUNDLE_FILE_LOCATOR_HELPER = (BundleFileLocatorHelper)Class.forName(BundleFileLocatorHelper.CLASS_NAME).newInstance();
- }
- catch (Throwable t)
- {
- // System.err.println("no jsp/jasper support");
- BUNDLE_FILE_LOCATOR_HELPER = new DefaultFileLocatorHelper();
- }
- }
- }
-
- /**
- * Removes quotes around system property values before we try to make them
- * into file pathes.
- */
- public static String stripQuotesIfPresent(String filePath)
- {
- if (filePath == null)
- return null;
-
- if ((filePath.startsWith("\"") || filePath.startsWith("'")) && (filePath.endsWith("\"") || filePath.endsWith("'")))
- return filePath.substring(1,filePath.length() - 1);
- return filePath;
- }
-
- /**
- * Look for the home directory of jetty as defined by the system property
- * 'jetty.home'. If undefined, look at the current bundle and uses its own
- * jettyhome folder for this feature.
- * <p>
- * Special case: inside eclipse-SDK:<br/>
- * If the bundle is jarred, see if we are inside eclipse-PDE itself. In that
- * case, look for the installation directory of eclipse-PDE, try to create a
- * jettyhome folder there and install the sample jettyhome folder at that
- * location. This makes the installation in eclipse-SDK easier. <br/>
- * This is a bit redundant with the work done by the jetty configuration
- * launcher.
- * </p>
- *
- * @param context
- * @throws Exception
- */
- public void setup(BundleContext context, Map<String, String> configProperties) throws Exception
- {
- File _installLocation = BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(context.getBundle());
- // debug:
- // new File("~/proj/eclipse-install/eclipse-3.5.1-SDK-jetty7/" +
- // "dropins/jetty7/plugins/org.eclipse.jetty.osgi.boot_0.0.1.001-SNAPSHOT.jar");
- boolean bootBundleCanBeJarred = true;
- String jettyHome = stripQuotesIfPresent(System.getProperty("jetty.home"));
-
- if (jettyHome == null || jettyHome.length() == 0)
- {
- if (_installLocation.getName().endsWith(".jar"))
- {
- jettyHome = JettyHomeHelper.setupJettyHomeInEclipsePDE(_installLocation);
- }
- if (jettyHome == null)
- {
- jettyHome = _installLocation.getAbsolutePath() + "/jettyhome";
- bootBundleCanBeJarred = false;
- }
- }
- // in case we stripped the quotes.
- System.setProperty("jetty.home",jettyHome);
-
- String jettyLogs = stripQuotesIfPresent(System.getProperty("jetty.logs"));
- if (jettyLogs == null || jettyLogs.length() == 0)
- {
- System.setProperty("jetty.logs",jettyHome + "/logs");
- }
-
- if (!bootBundleCanBeJarred && !_installLocation.isDirectory())
- {
- String install = _installLocation != null?_installLocation.getCanonicalPath():" unresolved_install_location";
- throw new IllegalArgumentException("The system property -Djetty.home" + " must be set to a directory or the bundle "
- + context.getBundle().getSymbolicName() + " installed here " + install + " must be unjarred.");
-
- }
- try
- {
- System.err.println("JETTY_HOME set to " + new File(jettyHome).getCanonicalPath());
- }
- catch (Throwable t)
- {
- System.err.println("JETTY_HOME _set to " + new File(jettyHome).getAbsolutePath());
- }
-
- 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
-
- File jettyHomeF = new File(jettyHome);
- URLClassLoader libExtClassLoader = null;
- try
- {
- libExtClassLoader = LibExtClassLoaderHelper.createLibEtcClassLoaderHelper(jettyHomeF,_server,
- JettyBootstrapActivator.class.getClassLoader());
- }
- catch (MalformedURLException e)
- {
- e.printStackTrace();
- }
-
- Thread.currentThread().setContextClassLoader(libExtClassLoader);
-
- String jettyetc = System.getProperty(OSGiWebappConstants.SYS_PROP_JETTY_ETC_FILES,"etc/jetty.xml");
- StringTokenizer tokenizer = new StringTokenizer(jettyetc,";,");
-
- Map<Object,Object> id_map = new HashMap<Object,Object>();
- id_map.put("Server",_server);
- Map<Object,Object> properties = new HashMap<Object,Object>();
- properties.put("jetty.home",jettyHome);
- properties.put("jetty.host",System.getProperty("jetty.host",""));
- properties.put("jetty.port",System.getProperty("jetty.port","8080"));
- properties.put("jetty.port.ssl",System.getProperty("jetty.port.ssl","8443"));
-
- while (tokenizer.hasMoreTokens())
- {
- String etcFile = tokenizer.nextToken().trim();
- File conffile = etcFile.startsWith("/")?new File(etcFile):new File(jettyHomeF,etcFile);
- if (!conffile.exists())
- {
- __logger.warn("Unable to resolve the jetty/etc file " + etcFile);
-
- if ("etc/jetty.xml".equals(etcFile))
- {
- // Missing jetty.xml file, so create a minimal Jetty configuration
- __logger.info("Configuring default server on 8080");
- SelectChannelConnector connector = new SelectChannelConnector();
- connector.setPort(8080);
- _server.addConnector(connector);
-
- HandlerCollection handlers = new HandlerCollection();
- ContextHandlerCollection contexts = new ContextHandlerCollection();
- RequestLogHandler requestLogHandler = new RequestLogHandler();
- handlers.setHandlers(new Handler[] { contexts, new DefaultHandler(), requestLogHandler });
- _server.setHandler(handlers);
- }
- }
- else
- {
- try
- {
- // Execute a Jetty configuration file
- XmlConfiguration config = new XmlConfiguration(new FileInputStream(conffile));
- 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 " + etcFile,saxparse);
- throw saxparse;
- }
- }
- }
-
- 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,getJarsWithTlds());
- }
- catch (MalformedURLException e)
- {
- e.printStackTrace();
- }
-
-
- _server.start();
- }
- catch (Throwable t)
- {
- t.printStackTrace();
- }
- finally
- {
- Thread.currentThread().setContextClassLoader(contextCl);
- }
-
- }
-
- /**
- * 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");
-
-
- }
-
- /**
- * Deploy a new web application on the jetty server.
- *
- * @param context
- * The current bundle context
- * @param webappFolderPath
- * The path to the root of the webapp. Must be a path relative to
- * bundle; either an absolute path.
- * @param contextPath
- * The context path. Must start with "/"
- * @param classInBundle
- * A class that belongs to the current bundle to inherit from the
- * osgi classloader. Null to not have access to the OSGI
- * classloader.
- * @throws Exception
- */
- public ContextHandler registerWebapplication(Bundle bundle, String webappFolderPath, String contextPath, String extraClasspath,
- String overrideBundleInstallLocation, String webXmlPath, String defaultWebXmlPath) throws Exception
- {
- File bundleInstall = overrideBundleInstallLocation == null?BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(bundle):new File(
- overrideBundleInstallLocation);
- File webapp = null;
- if (webappFolderPath != null && webappFolderPath.length() != 0 && !webappFolderPath.equals("."))
- {
- if (webappFolderPath.startsWith("/") || webappFolderPath.startsWith("file:/"))
- {
- webapp = new File(webappFolderPath);
- }
- else
- {
- webapp = new File(bundleInstall,webappFolderPath);
- }
- }
- else
- {
- webapp = bundleInstall;
- }
- if (!webapp.exists())
- {
- throw new IllegalArgumentException("Unable to locate " + webappFolderPath + " inside "
- + (bundleInstall != null?bundleInstall.getAbsolutePath():"unlocated bundle '" + bundle.getSymbolicName() + "'"));
- }
- return registerWebapplication(bundle,webapp,contextPath,extraClasspath,bundleInstall,webXmlPath,defaultWebXmlPath);
- }
-
- /**
- * @See {@link WebAppDeployer#scan()}
- * TODO: refacotr this into the createContext method of OSGiAppProvider.
- *
- * @param webapp
- * @param contextPath
- * @param classInBundle
- * @return The contexthandler created and started
- * @throws Exception
- */
- public ContextHandler registerWebapplication(Bundle contributor, File webapp, String contextPath, String extraClasspath, File bundleInstall,
- String webXmlPath, String defaultWebXmlPath) throws Exception
- {
-
- ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
- String[] oldServerClasses = null;
- WebAppContext context = null;
- try
- {
- // make sure we provide access to all the jetty bundles by going
- // through this bundle.
- OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
- // configure with access to all jetty classes and also all the classes
- // that the contributor gives access to.
- Thread.currentThread().setContextClassLoader(composite);
-
- context = new WebAppContext(webapp.getAbsolutePath(),contextPath);
- context.setExtraClasspath(extraClasspath);
-
- if (webXmlPath != null && webXmlPath.length() != 0)
- {
- File webXml = null;
- if (webXmlPath.startsWith("/") || webXmlPath.startsWith("file:/"))
- {
- webXml = new File(webXmlPath);
- }
- else
- {
- webXml = new File(bundleInstall,webXmlPath);
- }
- if (webXml.exists())
- {
- context.setDescriptor(webXml.getAbsolutePath());
- }
- }
-
- if (defaultWebXmlPath == null || defaultWebXmlPath.length() == 0)
- {
- //use the one defined by the OSGiAppProvider.
- defaultWebXmlPath = _provider.getDefaultsDescriptor();
- }
- if (defaultWebXmlPath != null && defaultWebXmlPath.length() != 0)
- {
- File defaultWebXml = null;
- if (defaultWebXmlPath.startsWith("/") || defaultWebXmlPath.startsWith("file:/"))
- {
- defaultWebXml = new File(webXmlPath);
- }
- else
- {
- defaultWebXml = new File(bundleInstall,defaultWebXmlPath);
- }
- if (defaultWebXml.exists())
- {
- context.setDefaultsDescriptor(defaultWebXml.getAbsolutePath());
- }
- }
-
- //other parameters that might be defines on the OSGiAppProvider:
- context.setParentLoaderPriority(_provider.isParentLoaderPriority());
-
- configureWebAppContext(context,contributor);
- configureWebappClassLoader(contributor,context,composite);
-
- // @see
- // org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext)
- // during initialization of the webapp all the jetty packages are
- // visible
- // through the webapp classloader.
- oldServerClasses = context.getServerClasses();
- context.setServerClasses(null);
- _provider.addContext(context);
-
- return context;
- }
- finally
- {
- if (context != null && oldServerClasses != null)
- {
- context.setServerClasses(oldServerClasses);
- }
- Thread.currentThread().setContextClassLoader(contextCl);
- }
-
- }
-
- /**
- * Stop a ContextHandler and remove it from the collection.
- *
- * @See ContextDeployer#undeploy
- * @param contextHandler
- * @throws Exception
- */
- public void unregister(ContextHandler contextHandler) throws Exception
- {
- contextHandler.stop();
- _ctxtHandler.removeHandler(contextHandler);
- }
-
- /**
- * @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();
- }
-
- /**
- * This type of registration relies on jetty's complete context xml file.
- * Context encompasses jndi and all other things. This makes the definition
- * of the webapp a lot more self-contained.
- *
- * @param webapp
- * @param contextPath
- * @param classInBundle
- * @throws Exception
- */
- public ContextHandler registerContext(Bundle contributor, String contextFileRelativePath, String extraClasspath, String overrideBundleInstallLocation)
- throws Exception
- {
- File contextsHome = _provider.getContextXmlDirAsFile();
- if (contextsHome != null)
- {
- File prodContextFile = new File(contextsHome,contributor.getSymbolicName() + "/" + contextFileRelativePath);
- if (prodContextFile.exists())
- {
- return registerContext(contributor,prodContextFile,extraClasspath,overrideBundleInstallLocation);
- }
- }
- File contextFile = overrideBundleInstallLocation != null?new File(overrideBundleInstallLocation,contextFileRelativePath):new File(
- BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(contributor),contextFileRelativePath);
- if (contextFile.exists())
- {
- return registerContext(contributor,contextFile,extraClasspath,overrideBundleInstallLocation);
- }
- else
- {
- if (contextFileRelativePath.startsWith("./"))
- {
- contextFileRelativePath = contextFileRelativePath.substring(1);
- }
- if (!contextFileRelativePath.startsWith("/"))
- {
- contextFileRelativePath = "/" + contextFileRelativePath;
- }
- if (overrideBundleInstallLocation == null)
- {
- URL contextURL = contributor.getEntry(contextFileRelativePath);
- if (contextURL != null)
- {
- return registerContext(contributor,contextURL.openStream(),extraClasspath,overrideBundleInstallLocation);
- }
- }
- else
- {
- JarFile zipFile = null;
- try
- {
- zipFile = new JarFile(overrideBundleInstallLocation);
- ZipEntry entry = zipFile.getEntry(contextFileRelativePath.substring(1));
- return registerContext(contributor,zipFile.getInputStream(entry),extraClasspath,overrideBundleInstallLocation);
- }
- catch (Throwable t)
- {
-
- }
- finally
- {
- if (zipFile != null)
- try
- {
- zipFile.close();
- }
- catch (IOException ioe)
- {
- }
- }
- }
- throw new IllegalArgumentException("Could not find the context " + "file " + contextFileRelativePath + " for the bundle "
- + contributor.getSymbolicName() + (overrideBundleInstallLocation != null?" using the install location " + overrideBundleInstallLocation:""));
- }
- }
-
- /**
- * This type of registration relies on jetty's complete context xml file.
- * Context encompasses jndi and all other things. This makes the definition
- * of the webapp a lot more self-contained.
- *
- * @param webapp
- * @param contextPath
- * @param classInBundle
- * @throws Exception
- */
- private ContextHandler registerContext(Bundle contributor, File contextFile, String extraClasspath, String overrideBundleInstallLocation) throws Exception
- {
- InputStream contextFileInputStream = null;
- try
- {
- contextFileInputStream = new BufferedInputStream(new FileInputStream(contextFile));
- return registerContext(contributor,contextFileInputStream,extraClasspath,overrideBundleInstallLocation);
- }
- finally
- {
- if (contextFileInputStream != null)
- try
- {
- contextFileInputStream.close();
- }
- catch (IOException ioe)
- {
- }
- }
- }
-
- /**
- * @param contributor
- * @param contextFileInputStream
- * @return The ContextHandler created and registered or null if it did not
- * happen.
- * @throws Exception
- */
- private ContextHandler registerContext(Bundle contributor, InputStream contextFileInputStream, String extraClasspath, String overrideBundleInstallLocation)
- throws Exception
- {
- ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
- String[] oldServerClasses = null;
- WebAppContext webAppContext = null;
- try
- {
- // make sure we provide access to all the jetty bundles by going
- // through this bundle.
- OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
- // configure with access to all jetty classes and also all the
- // classes
- // that the contributor gives access to.
- Thread.currentThread().setContextClassLoader(composite);
- ContextHandler context = createContextHandler(contributor,contextFileInputStream,extraClasspath,overrideBundleInstallLocation);
- if (context == null)
- {
- return null;// did not happen
- }
-
- // ok now register this webapp. we checked when we started jetty
- // that there
- // was at least one such handler for webapps.
- //the actual registration must happen via the new Deployment API.
-// _ctxtHandler.addHandler(context);
-
- configureWebappClassLoader(contributor,context,composite);
- if (context instanceof WebAppContext)
- {
- webAppContext = (WebAppContext)context;
- // @see
- // org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext)
- oldServerClasses = webAppContext.getServerClasses();
- webAppContext.setServerClasses(null);
- }
-
- context.start();
- return context;
- }
- finally
- {
- if (webAppContext != null)
- {
- webAppContext.setServerClasses(oldServerClasses);
- }
- Thread.currentThread().setContextClassLoader(contextCl);
- }
-
- }
-
- /**
- * 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 : JSP_REGISTRATION_HELPERS)
- {
- URL[] urls = regCustomizer.getJarsWithTlds(_provider, 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;
- }
-
- /**
- * Applies the properties of WebAppDeployer as defined in jetty.xml.
- *
- * @see {WebAppDeployer#scan} around the comment
- * <code>// configure it</code>
- */
- protected void configureWebAppContext(WebAppContext wah, Bundle contributor)
- {
- // rfc66
- wah.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT,contributor.getBundleContext());
-
- //spring-dm-1.2.1 looks for the BundleContext as a different attribute.
- //not a spec... but if we want to support
- //org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
- //then we need to do this to:
- wah.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(),
- contributor.getBundleContext());
-
- }
-
- /**
- * @See {@link ContextDeployer#scan}
- * @param contextFile
- * @return
- */
- protected ContextHandler createContextHandler(Bundle bundle, File contextFile, String extraClasspath, String overrideBundleInstallLocation)
- {
- try
- {
- return createContextHandler(bundle,new BufferedInputStream(new FileInputStream(contextFile)),extraClasspath,overrideBundleInstallLocation);
- }
- catch (FileNotFoundException e)
- {
- e.printStackTrace();
- }
- return null;
- }
-
- /**
- * @See {@link ContextDeployer#scan}
- * @param contextFile
- * @return
- */
- @SuppressWarnings("unchecked")
- protected ContextHandler createContextHandler(Bundle bundle, InputStream contextInputStream, String extraClasspath, String overrideBundleInstallLocation)
- {
- /*
- * Do something identical to what the ContextProvider would have done:
- * XmlConfiguration xmlConfiguration=new
- * XmlConfiguration(resource.getURL()); HashMap properties = new
- * HashMap(); properties.put("Server", _contexts.getServer()); if
- * (_configMgr!=null) properties.putAll(_configMgr.getProperties());
- *
- * xmlConfiguration.setProperties(properties); ContextHandler
- * context=(ContextHandler)xmlConfiguration.configure();
- * context.setAttributes(new AttributesMap(_contextAttributes));
- */
- try
- {
- XmlConfiguration xmlConfiguration = new XmlConfiguration(contextInputStream);
- HashMap properties = new HashMap();
- properties.put("Server",_server);
-
- // insert the bundle's location as a property.
- setThisBundleHomeProperty(bundle,properties,overrideBundleInstallLocation);
- xmlConfiguration.setProperties(properties);
-
- ContextHandler context = (ContextHandler)xmlConfiguration.configure();
- if (context instanceof WebAppContext)
- {
- ((WebAppContext)context).setExtraClasspath(extraClasspath);
- ((WebAppContext)context).setParentLoaderPriority(_provider.isParentLoaderPriority());
- if (_provider.getDefaultsDescriptor() != null && _provider.getDefaultsDescriptor().length() != 0)
- {
- ((WebAppContext)context).setDefaultsDescriptor(_provider.getDefaultsDescriptor());
- }
- }
-
- // rfc-66:
- context.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT,bundle.getBundleContext());
-
- //spring-dm-1.2.1 looks for the BundleContext as a different attribute.
- //not a spec... but if we want to support
- //org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
- //then we need to do this to:
- context.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(),
- bundle.getBundleContext());
- return context;
- }
- catch (FileNotFoundException e)
- {
- return null;
- }
- catch (SAXException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- catch (IOException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- catch (Throwable e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- finally
- {
- if (contextInputStream != null)
- try
- {
- contextInputStream.close();
- }
- catch (IOException ioe)
- {
- }
- }
- return null;
- }
-
- /**
- * Configure a classloader onto the context. If the context is a
- * WebAppContext, build a WebAppClassLoader that has access to all the jetty
- * classes thanks to the classloader of the JettyBootStrapper bundle and
- * also has access to the classloader of the bundle that defines this
- * context.
- * <p>
- * If the context is not a WebAppContext, same but with a simpler
- * URLClassLoader. Note that the URLClassLoader is pretty much fake: it
- * delegate all actual classloading to the parent classloaders.
- * </p>
- * <p>
- * The URL[] returned by the URLClassLoader create contained specifically
- * the jars that some j2ee tools expect and look into. For example the jars
- * that contain tld files for jasper's jstl support.
- * </p>
- * <p>
- * Also as the jars in the lib folder and the classes in the classes folder
- * might already be in the OSGi classloader we filter them out of the
- * WebAppClassLoader
- * </p>
- *
- * @param context
- * @param contributor
- * @param webapp
- * @param contextPath
- * @param classInBundle
- * @throws Exception
- */
- protected void configureWebappClassLoader(Bundle contributor, ContextHandler context, OSGiWebappClassLoader webappClassLoader) throws Exception
- {
- if (context instanceof WebAppContext)
- {
- WebAppContext webappCtxt = (WebAppContext)context;
- context.setClassLoader(webappClassLoader);
- webappClassLoader.setWebappContext(webappCtxt);
- }
- else
- {
- context.setClassLoader(webappClassLoader);
- }
- }
-
- /**
- * No matter what the type of webapp, we create a WebappClassLoader.
- */
- protected OSGiWebappClassLoader createWebappClassLoader(Bundle contributor) throws Exception
- {
- // we use a temporary WebAppContext object.
- // if this is a real webapp we will set it on it a bit later: once we
- // know.
- OSGiWebappClassLoader webappClassLoader = new OSGiWebappClassLoader(_commonParentClassLoaderForWebapps,new WebAppContext(),contributor);
- return webappClassLoader;
- }
-
- /**
- * Set the property &quot;this.bundle.install&quot; to point to the location
- * of the bundle. Useful when <SystemProperty name="this.bundle.home"/> is
- * used.
- */
- private void setThisBundleHomeProperty(Bundle bundle, HashMap<String, Object> properties, String overrideBundleInstallLocation)
- {
- try
- {
- File location = overrideBundleInstallLocation != null?new File(overrideBundleInstallLocation):BUNDLE_FILE_LOCATOR_HELPER
- .getBundleInstallLocation(bundle);
- properties.put("this.bundle.install",location.getCanonicalPath());
- }
- catch (Throwable t)
- {
- System.err.println("Unable to set 'this.bundle.install' " + " for the bundle " + bundle.getSymbolicName());
- t.printStackTrace();
- }
- }
-
-
-}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java
index 8aef238472..820a627e1f 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java
@@ -13,6 +13,8 @@
package org.eclipse.jetty.osgi.boot.utils;
import java.io.File;
+import java.net.URL;
+import java.util.Enumeration;
import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
import org.osgi.framework.Bundle;
@@ -53,7 +55,7 @@ public interface BundleFileLocatorHelper
*
* @param bundle
* @param path
- * @return
+ * @return file object
* @throws Exception
*/
public File getFileInBundle(Bundle bundle, String path) throws Exception;
@@ -73,5 +75,16 @@ public interface BundleFileLocatorHelper
* embedded inside it.
*/
public File[] locateJarsInsideBundle(Bundle bundle) throws Exception;
+
+
+ /**
+ * Helper method equivalent to Bundle#getEntry(String entryPath) except that
+ * it searches for entries in the fragments by using the findEntries method.
+ *
+ * @param bundle
+ * @param entryPath
+ * @return null or all the entries found for that path.
+ */
+ public Enumeration<URL> findEntries(Bundle bundle, String entryPath);
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java
index 5db0a2f4de..1a37a046c6 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java
@@ -46,7 +46,7 @@ public interface WebappRegistrationCustomizer
* the root and/or in the lib folder (nice for PDE developement situations)
* Unsupported: the bundle is a jar that embeds more jars.
*
- * @return
+ * @return array of URLs
* @throws Exception
*/
URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper fileLocator) throws Exception;
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
index a3541b60ac..a918cd7706 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
@@ -14,6 +14,8 @@ package org.eclipse.jetty.osgi.boot.utils.internal;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLConnection;
import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelper;
import org.osgi.framework.Bundle;
@@ -59,7 +61,7 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
* Assuming the bundle is started.
*
* @param bundle
- * @return
+ * @return classloader object
*/
public ClassLoader getBundleClassLoader(Bundle bundle)
{
@@ -180,4 +182,5 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
}
return null;
}
+
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
index 5dfc3676df..ec8f997d69 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
@@ -14,10 +14,13 @@ package org.eclipse.jetty.osgi.boot.utils.internal;
import java.io.File;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
+import java.net.URLDecoder;
import java.util.ArrayList;
+import java.util.Enumeration;
import java.util.zip.ZipFile;
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
@@ -66,8 +69,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
// grab the MANIFEST.MF's url
// and then do what it takes.
URL url = bundle.getEntry("/META-INF/MANIFEST.MF");
- // System.err.println(url.toString() + " " + url.toURI() + " " +
- // url.getProtocol());
+// System.err.println(url.toString() + " " + url.toURI() + " " + url.getProtocol());
if (url.getProtocol().equals("file"))
{
// some osgi frameworks do use the file protocole directly in some
@@ -130,11 +132,36 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
{
// observed this on felix-2.0.0
String location = bundle.getLocation();
+// System.err.println("location " + location);
if (location.startsWith("file:/"))
{
URI uri = new URI(URIUtil.encodePath(location));
return new File(uri);
}
+ else if (location.startsWith("file:"))
+ {
+ //location defined in the BundleArchive m_bundleArchive
+ //it is relative to relative to the BundleArchive's m_archiveRootDir
+ File res = new File(location.substring("file:".length()));
+ if (!res.exists())
+ {
+ return null;
+// Object bundleArchive = getFelixBundleArchive(bundle);
+// File archiveRoot = getFelixBundleArchiveRootDir(bundleArchive);
+// String currentLocation = getFelixBundleArchiveCurrentLocation(bundleArchive);
+// System.err.println("Got the archive root " + archiveRoot.getAbsolutePath()
+// + " current location " + currentLocation + " is directory ?");
+// res = new File(archiveRoot, currentLocation != null
+// ? currentLocation : location.substring("file:".length()));
+ }
+ return res;
+ }
+ else if (location.startsWith("reference:file:"))
+ {
+ location = URLDecoder.decode(location.substring("reference:".length()), "UTF-8");
+ File file = new File(location.substring("file:".length()));
+ return file;
+ }
}
return null;
}
@@ -144,7 +171,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
*
* @param bundle
* @param path
- * @return
+ * @return file object
* @throws Exception
*/
public File getFileInBundle(Bundle bundle, String path) throws Exception
@@ -162,6 +189,29 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
}
return webapp;
}
+
+ /**
+ * Helper method equivalent to Bundle#getEntry(String entryPath) except that
+ * it searches for entries in the fragments by using the Bundle#findEntries method.
+ *
+ * @param bundle
+ * @param entryPath
+ * @return null or all the entries found for that path.
+ */
+ public Enumeration<URL> findEntries(Bundle bundle, String entryPath)
+ {
+ int last = entryPath.lastIndexOf('/');
+ String path = last != -1 && last < entryPath.length() -2
+ ? entryPath.substring(0, last) : "/";
+ if (!path.startsWith("/"))
+ {
+ path = "/" + path;
+ }
+ String pattern = last != -1 && last < entryPath.length() -2
+ ? entryPath.substring(last+1) : entryPath;
+ Enumeration<URL> enUrls = bundle.findEntries(path, pattern, false);
+ return enUrls;
+ }
/**
* If the bundle is a jar, returns the jar. If the bundle is a folder, look
@@ -205,9 +255,78 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
}
else
{
- return new File[]
- { jasperLocation };
+ return new File[] { jasperLocation };
}
}
+
+
+ //introspection on equinox to invoke the getLocalURL method on BundleURLConnection
+ //equivalent to using the FileLocator without depending on an equinox class.
+ private static Method BUNDLE_URL_CONNECTION_getLocalURL = null;
+ private static Method BUNDLE_URL_CONNECTION_getFileURL = null;
+ /**
+ * Only useful for equinox: on felix we get the file:// or jar:// url already.
+ * Other OSGi implementations have not been tested
+ * <p>
+ * Get a URL to the bundle entry that uses a common protocol (i.e. file:
+ * jar: or http: etc.).
+ * </p>
+ * @return a URL to the bundle entry that uses a common protocol
+ */
+ public static URL getLocalURL(URL url) {
+ if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol())) {
+ try {
+ URLConnection conn = url.openConnection();
+ if (BUNDLE_URL_CONNECTION_getLocalURL == null &&
+ conn.getClass().getName().equals(
+ "org.eclipse.osgi.framework.internal.core.BundleURLConnection")) {
+ BUNDLE_URL_CONNECTION_getLocalURL = conn.getClass().getMethod("getLocalURL", null);
+ BUNDLE_URL_CONNECTION_getLocalURL.setAccessible(true);
+ }
+ if (BUNDLE_URL_CONNECTION_getLocalURL != null) {
+ return (URL)BUNDLE_URL_CONNECTION_getLocalURL.invoke(conn, null);
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+ return url;
+ }
+ /**
+ * Only useful for equinox: on felix we get the file:// url already.
+ * Other OSGi implementations have not been tested
+ * <p>
+ * Get a URL to the content of the bundle entry that uses the file: protocol.
+ * The content of the bundle entry may be downloaded or extracted to the local
+ * file system in order to create a file: URL.
+ * @return a URL to the content of the bundle entry that uses the file: protocol
+ * </p>
+ */
+ public static URL getFileURL(URL url)
+ {
+ if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
+ {
+ try
+ {
+ URLConnection conn = url.openConnection();
+ if (BUNDLE_URL_CONNECTION_getFileURL == null &&
+ conn.getClass().getName().equals(
+ "org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
+ {
+ BUNDLE_URL_CONNECTION_getFileURL = conn.getClass().getMethod("getFileURL", null);
+ BUNDLE_URL_CONNECTION_getFileURL.setAccessible(true);
+ }
+ if (BUNDLE_URL_CONNECTION_getFileURL != null)
+ {
+ return (URL)BUNDLE_URL_CONNECTION_getFileURL.invoke(conn, null);
+ }
+ }
+ catch (Throwable t)
+ {
+ t.printStackTrace();
+ }
+ }
+ return url;
+ }
}

Back to the top