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
This commit is contained in:
parent
8e744c0595
commit
d031732aec
|
@ -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
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
return app.getContextHandler();
|
||||
// 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");
|
||||
}
|
||||
// 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)
|
||||
{
|
||||
((WebAppContext)wah).setConfigurationClasses(_configurationClasses);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <autoInstallOSGiBundles>true</autoInstallOSGiBundles>
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue