Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHugues Malphettes2011-05-30 09:58:18 +0000
committerHugues Malphettes2011-05-30 09:58:18 +0000
commitd031732aece9e83671ee4f8f834a38fd70bc5af1 (patch)
treea5983ef3f8b65a22c0e77e082a8a5153c17abe88
parent8e744c05955c0557420972df34b15c90a3f47d1e (diff)
downloadorg.eclipse.jetty.project-d031732aece9e83671ee4f8f834a38fd70bc5af1.tar.gz
org.eclipse.jetty.project-d031732aece9e83671ee4f8f834a38fd70bc5af1.tar.xz
org.eclipse.jetty.project-d031732aece9e83671ee4f8f834a38fd70bc5af1.zip
bug 347617 Dynamically install/update/remove OSGi bundles discovered in the contexts folder
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@3349 7e9141cc-0065-0410-87d8-b60c137991c4
-rw-r--r--VERSION.txt3
-rw-r--r--jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-nested-default.xml5
-rw-r--r--jetty-osgi/jetty-osgi-boot/pom.xml2
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java36
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiAppProvider.java333
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java27
6 files changed, 382 insertions, 24 deletions
diff --git a/VERSION.txt b/VERSION.txt
index 42553deafa..198ca0feb6 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1,3 +1,6 @@
+jetty-7.4.3-SNAPSHOT
+ + 347617 Dynamically install/update/remove OSGi bundles discovered in the contexts folder
+
jetty-7.4.2.v20110526
+ 334443 Improve the ability to specify extra class paths using the Jetty Maven Plugin
+ 336220 tmp directory is not set if you reload a webapp with jetty-maven-plugin
diff --git a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-nested-default.xml b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-nested-default.xml
index a732f69a48..67d84e9df8 100644
--- a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-nested-default.xml
+++ b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-nested-default.xml
@@ -16,7 +16,10 @@
<Arg>
<New id="NestedConnector" class="org.eclipse.jetty.nested.NestedConnector">
<Set name="statsOn">false</Set>
- <Set name="confidentialPort">8443</Set>
+ <Set name="forwarded">true</Set>
+ <Set name="forwardedHostHeader">x-forwarded_for</Set>
+ <Set name="forwardedCipherSuiteHeader">sslclientcipher</Set>
+ <Set name="forwardedSslSessionIdHeader">sslsessionid</Set>
<Call name="addLifeCycleListener">
<Arg>
<New class="org.eclipse.jetty.osgi.nested.NestedConnectorListener" id="NestedConnectorListener">
diff --git a/jetty-osgi/jetty-osgi-boot/pom.xml b/jetty-osgi/jetty-osgi-boot/pom.xml
index ed786dfa0f..9ac43c6d4f 100644
--- a/jetty-osgi/jetty-osgi-boot/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot/pom.xml
@@ -157,7 +157,7 @@
</executions>
<configuration>
<instructions>
- <Bundle-SymbolicName>org.eclipse.jetty.osgi.boot;singleton:=true</Bundle-SymbolicName>
+ <Bundle-SymbolicName>org.eclipse.jetty.osgi.boot</Bundle-SymbolicName>
<Export-Package>org.eclipse.jetty.osgi.boot;version="${parsedVersion.osgiVersion}",org.eclipse.jetty.osgi.boot.utils,org.eclipse.jetty.osgi.nested;version="${parsedVersion.osgiVersion}"</Export-Package>
<Bundle-Activator>org.eclipse.jetty.osgi.boot.JettyBootstrapActivator</Bundle-Activator>
<!-- disable the uses directive: jetty will accomodate pretty much any versions
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 74c910c525..e3c0f9040d 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
@@ -70,6 +70,7 @@ public class JettyBootstrapActivator implements BundleActivator
private JettyContextHandlerServiceTracker _jettyContextHandlerTracker;
private PackageAdminServiceTracker _packageAdminServiceTracker;
private BundleTracker _webBundleTracker;
+ private BundleContext _bundleContext;
// private ServiceRegistration _jettyServerFactoryService;
private JettyServerServiceTracker _jettyServerServiceTracker;
@@ -86,6 +87,7 @@ public class JettyBootstrapActivator implements BundleActivator
public void start(BundleContext context) throws Exception
{
INSTANCE = this;
+ _bundleContext = context;
// track other bundles and fragments attached to this bundle that we
// should activate.
@@ -298,18 +300,28 @@ public class JettyBootstrapActivator implements BundleActivator
*/
private static void checkBundleActivated()
{
- if (INSTANCE == null) {
- Bundle thisBundle = FrameworkUtil.getBundle(JettyBootstrapActivator.class);
- try
- {
- thisBundle.start();
- }
- catch (BundleException e)
- {
- //nevermind.
- }
- }
+ if (INSTANCE == null)
+ {
+ Bundle thisBundle = FrameworkUtil.getBundle(JettyBootstrapActivator.class);
+ try
+ {
+ thisBundle.start();
+ }
+ catch (BundleException e)
+ {
+ // nevermind.
+ }
+ }
+ }
+
+ /**
+ * @return The bundle context for this bundle.
+ */
+ public static BundleContext getBundleContext()
+ {
+ checkBundleActivated();
+ return INSTANCE._bundleContext;
}
-} \ 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 efb27ee819..5bebe2e852 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
@@ -17,17 +17,26 @@ package org.eclipse.jetty.osgi.boot;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.Map.Entry;
+import java.util.Set;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.providers.ContextProvider;
import org.eclipse.jetty.deploy.providers.ScanningAppProvider;
+import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.Scanner;
+import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
/**
* AppProvider for OSGi. Supports the configuration of ContextHandlers and
@@ -42,6 +51,12 @@ import org.osgi.framework.Bundle;
* it supports the deployment of WebAppContexts. Except for the scanning of the
* webapps directory.
* </p>
+ * <p>
+ * When the parameter autoInstallOSGiBundles is set to true, OSGi bundles that
+ * are located in the monitored directory are installed and started after the
+ * framework as finished auto-starting all the other bundles.
+ * Warning: only use this for development.
+ * </p>
*/
public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
{
@@ -50,7 +65,14 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
private boolean _parentLoaderPriority = false;
private String _defaultsDescriptor;
private String _tldBundles;
+ private String[] _configurationClasses;
+
+ private boolean _autoInstallOSGiBundles = true;
+ //Keep track of the bundles that were installed and that are waiting for the
+ //framework to complete its initialization.
+ Set<Bundle> _pendingBundlesToStart = null;
+
/**
* When a context file corresponds to a deployed bundle and is changed we
* reload the corresponding bundle.
@@ -58,10 +80,15 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
private static class Filter implements FilenameFilter
{
OSGiAppProvider _enclosedInstance;
-
+
public boolean accept(File dir, String name)
{
- if (!new File(dir,name).isDirectory())
+ File file = new File(dir,name);
+ if (fileMightBeAnOSGiBundle(file))
+ {
+ return true;
+ }
+ if (!file.isDirectory())
{
String contextName = getDeployedAppName(name);
if (contextName != null)
@@ -144,15 +171,20 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
public ContextHandler createContextHandler(App app) throws Exception
{
// return pre-created Context
- if (app.getContextHandler() != null)
+ ContextHandler wah = app.getContextHandler();
+ if (wah == null)
+ {
+ // for some reason it was not defined when the App was constructed.
+ // we don't support this situation at this point.
+ // once the WebAppRegistrationHelper is refactored, the code
+ // that creates the ContextHandler will actually be here.
+ throw new IllegalStateException("The App must be passed the " + "instance of the ContextHandler when it is construsted");
+ }
+ if (_configurationClasses != null && wah instanceof WebAppContext)
{
- return app.getContextHandler();
+ ((WebAppContext)wah).setConfigurationClasses(_configurationClasses);
}
- // for some reason it was not defined when the App was constructed.
- // we don't support this situation at this point.
- // once the WebAppRegistrationHelper is refactored, the code
- // that creates the ContextHandler will actually be here.
- throw new IllegalStateException("The App must be passed the " + "instance of the ContextHandler when it is construsted");
+ return app.getContextHandler();
}
/**
@@ -187,7 +219,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
// TODO apply configuration specific to this provider
if (context instanceof WebAppContext)
{
- ((WebAppContext)context).setExtractWAR(isExtract());
+ ((WebAppContext)context).setExtractWAR(isExtract());
}
// wrap context as an App
@@ -346,6 +378,24 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
_extractWars=extract;
}
+ /**
+ * @return true when this app provider locates osgi bundles and features in
+ * its monitored directory and installs them. false by default.
+ */
+ public boolean isAutoInstallOSGiBundles()
+ {
+ return _autoInstallOSGiBundles;
+ }
+
+ /**
+ * &lt;autoInstallOSGiBundles&gt;true&lt;/autoInstallOSGiBundles&gt;
+ * @param installingOSGiBundles
+ */
+ public void setAutoInstallOSGiBundles(boolean installingOSGiBundles)
+ {
+ _autoInstallOSGiBundles=installingOSGiBundles;
+ }
+
/* ------------------------------------------------------------ */
/**
@@ -355,6 +405,10 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
* directory, then the ContextXmlDir is examined to see if a foo.xml file
* exists. If it does, then this deployer will not deploy the webapp and the
* ContextProvider should be used to act on the foo.xml file.
+ * </p>
+ * <p>
+ * Also if this directory contains some osgi bundles, it will install them.
+ * </p>
*
* @see ContextProvider
* @param contextsDir
@@ -381,5 +435,264 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
{
return _tldBundles;
}
+
+ /**
+ * @param configurations The configuration class names.
+ */
+ public void setConfigurationClasses(String[] configurations)
+ {
+ _configurationClasses = configurations==null?null:(String[])configurations.clone();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ *
+ */
+ public String[] getConfigurationClasses()
+ {
+ return _configurationClasses;
+ }
+
+ /**
+ * Overridden to install the OSGi bundles found in the monitored folder.
+ */
+ protected void doStart() throws Exception
+ {
+ if (isAutoInstallOSGiBundles())
+ {
+ File scandir = getMonitoredDirResource().getFile();
+ for (File file : scandir.listFiles())
+ {
+ if (fileMightBeAnOSGiBundle(file))
+ {
+ installBundle(file, false);
+ }
+ }
+ }
+ super.doStart();
+ if (isAutoInstallOSGiBundles())
+ {
+ Scanner.ScanCycleListener scanCycleListner = new AutoStartWhenFrameworkHasCompleted(this);
+ super.addScannerListener(scanCycleListner);
+ }
+ }
+
+ /**
+ * When the file is a jar or a folder, we look if it looks like an OSGi bundle.
+ * In that case we install it and start it.
+ * <p>
+ * Really a simple trick to get going quickly with development.
+ * </p>
+ */
+ @Override
+ protected void fileAdded(String filename) throws Exception
+ {
+ File file = new File(filename);
+ if (isAutoInstallOSGiBundles() && file.exists() && fileMightBeAnOSGiBundle(file))
+ {
+ installBundle(file, true);
+ }
+ else
+ {
+ super.fileAdded(filename);
+ }
+ }
+
+ /**
+ * @param file
+ * @return
+ */
+ private static boolean fileMightBeAnOSGiBundle(File file)
+ {
+ if (file.isDirectory())
+ {
+ if (new File(file,"META-INF/MANIFEST.MF").exists())
+ {
+ return true;
+ }
+ }
+ else if (file.getName().endsWith(".jar"))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void fileChanged(String filename) throws Exception
+ {
+ File file = new File(filename);
+ if (isAutoInstallOSGiBundles() && fileMightBeAnOSGiBundle(file))
+ {
+ updateBundle(file);
+ }
+ else
+ {
+ super.fileChanged(filename);
+ }
+ }
+
+ @Override
+ protected void fileRemoved(String filename) throws Exception
+ {
+ File file = new File(filename);
+ if (isAutoInstallOSGiBundles() && fileMightBeAnOSGiBundle(file))
+ {
+ uninstallBundle(file);
+ }
+ else
+ {
+ super.fileRemoved(filename);
+ }
+ }
+
+ /**
+ * Returns a bundle according to its location.
+ * In the version 1.6 of org.osgi.framework, BundleContext.getBundle(String) is what we want.
+ * However to support older versions of OSGi. We use our own local refrence mechanism.
+ * @param location
+ * @return
+ */
+ protected Bundle getBundle(BundleContext bc, String location)
+ {
+ //not available in older versions of OSGi:
+ //return bc.getBundle(location);
+ for (Bundle b : bc.getBundles())
+ {
+ if (b.getLocation().equals(location))
+ {
+ return b;
+ }
+ }
+ return null;
+ }
+
+ protected synchronized Bundle installBundle(File file, boolean start)
+ {
+
+ try
+ {
+ BundleContext bc = JettyBootstrapActivator.getBundleContext();
+ String location = file.toURI().toString();
+ Bundle b = getBundle(bc, location);
+ if (b == null)
+ {
+ b = bc.installBundle(location);
+ }
+ if (b == null)
+ {
+ //not sure we will ever be here,
+ //most likely a BundleException was thrown
+ Log.warn("The file " + location + " is not an OSGi bundle.");
+ return null;
+ }
+ if (start && b.getHeaders().get(Constants.FRAGMENT_HOST) == null)
+ {//not a fragment, try to start it. if the framework has finished auto-starting.
+ if (!PackageAdminServiceTracker.INSTANCE.frameworkHasCompletedAutostarts())
+ {
+ if (_pendingBundlesToStart == null)
+ {
+ _pendingBundlesToStart = new HashSet<Bundle>();
+ }
+ _pendingBundlesToStart.add(b);
+ return null;
+ }
+ else
+ {
+ b.start();
+ }
+ }
+ return b;
+ }
+ catch (BundleException e)
+ {
+ Log.warn("Unable to " + (start? "start":"install") + " the bundle " + file.getAbsolutePath(), e);
+ }
+ return null;
+ }
+
+ protected void uninstallBundle(File file)
+ {
+ try
+ {
+ Bundle b = getBundle(JettyBootstrapActivator.getBundleContext(), file.toURI().toString());
+ b.stop();
+ b.uninstall();
+ }
+ catch (BundleException e)
+ {
+ Log.warn("Unable to uninstall the bundle " + file.getAbsolutePath(), e);
+ }
+ }
+
+ protected void updateBundle(File file)
+ {
+ try
+ {
+ Bundle b = getBundle(JettyBootstrapActivator.getBundleContext(), file.toURI().toString());
+ if (b == null)
+ {
+ installBundle(file, true);
+ }
+ else if (b.getState() == Bundle.ACTIVE)
+ {
+ b.update();
+ }
+ else
+ {
+ b.start();
+ }
+ }
+ catch (BundleException e)
+ {
+ Log.warn("Unable to update the bundle " + file.getAbsolutePath(), e);
+ }
+ }
+
+
+}
+/**
+ * At the end of each scan, if there are some bundles to be started,
+ * look if the framework has completed its autostart. In that case start those bundles.
+ */
+class AutoStartWhenFrameworkHasCompleted implements Scanner.ScanCycleListener
+{
+ private final OSGiAppProvider _appProvider;
+
+ AutoStartWhenFrameworkHasCompleted(OSGiAppProvider appProvider)
+ {
+ _appProvider = appProvider;
+ }
+
+ public void scanStarted(int cycle) throws Exception
+ {
+ }
+
+ public void scanEnded(int cycle) throws Exception
+ {
+ if (_appProvider._pendingBundlesToStart != null && PackageAdminServiceTracker.INSTANCE.frameworkHasCompletedAutostarts())
+ {
+ Iterator<Bundle> it = _appProvider._pendingBundlesToStart.iterator();
+ while (it.hasNext())
+ {
+ Bundle b = it.next();
+ if (b.getHeaders().get(Constants.FRAGMENT_HOST) != null)
+ {
+ continue;
+ }
+ try
+ {
+ b.start();
+ }
+ catch (BundleException e)
+ {
+ Log.warn("Unable to start the bundle " + b.getLocation(), e);
+ }
+
+ }
+ _appProvider._pendingBundlesToStart = null;
+ }
+ }
}
+
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java
index ce8127a60a..f6229531fe 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java
@@ -26,6 +26,7 @@ import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
/**
* When the PackageAdmin service is activated we can look for the fragments
@@ -38,6 +39,9 @@ public class PackageAdminServiceTracker implements ServiceListener
private List<BundleActivator> _activatedFragments = new ArrayList<BundleActivator>();
private boolean _fragmentsWereActivated = false;
+ //Use the deprecated StartLevel to stay compatible with older versions of OSGi.
+ private StartLevel _startLevel;
+ private int _maxStartLevel = 6;
public static PackageAdminServiceTracker INSTANCE = null;
public PackageAdminServiceTracker(BundleContext context)
@@ -66,6 +70,21 @@ public class PackageAdminServiceTracker implements ServiceListener
_fragmentsWereActivated = sr != null;
if (sr != null)
invokeFragmentActivators(sr);
+
+ sr = _context.getServiceReference(StartLevel.class.getName());
+ if (sr != null)
+ {
+ _startLevel = (StartLevel)_context.getService(sr);
+ try
+ {
+ _maxStartLevel = Integer.parseInt(System.getProperty("osgi.startLevel","6"));
+ }
+ catch (Exception e)
+ {
+ //nevermind default on the usual.
+ _maxStartLevel = 6;
+ }
+ }
return _fragmentsWereActivated;
}
@@ -273,5 +292,13 @@ public class PackageAdminServiceTracker implements ServiceListener
}
}
}
+
+ /**
+ * @return true if the framework has completed all the start levels.
+ */
+ public boolean frameworkHasCompletedAutostarts()
+ {
+ return _startLevel == null ? true : _startLevel.getStartLevel() >= _maxStartLevel;
+ }
}

Back to the top