committing to save changes but does not yet compile

This commit is contained in:
Jan Bartel 2013-02-20 09:26:30 +11:00
parent 39c5d6c141
commit 9af4334739
88 changed files with 10352 additions and 769 deletions

View File

@ -25,9 +25,12 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.osgi.boot.OSGiWebInfConfiguration;
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
import org.osgi.framework.Bundle;
@ -62,10 +65,10 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra
* @param provider
* @return
*/
private static Collection<String> getTldBundles(OSGiAppProvider provider)
private static Collection<String> getTldBundles(DeploymentManager deploymentManager)
{
String sysprop = System.getProperty(SYS_PROP_TLD_BUNDLES);
String att = (String) provider.getTldBundles();
String att = (String) deploymentManager.getContextAttribute(OSGiWebInfConfiguration.CONTAINER_BUNDLE_PATTERN);
if (sysprop == null && att == null) { return Collections.emptySet(); }
if (att == null)
{
@ -89,9 +92,8 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra
* @return The location of the jars that contain tld files. Jasper will
* discover them.
*/
public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception
public URL[] getJarsWithTlds(DeploymentManager deploymentManager, BundleFileLocatorHelper locatorHelper) throws Exception
{
List<URL> urls = new ArrayList<URL>();
// naive way of finding those bundles.
// lots of assumptions: for example we assume a single version of each
// bundle that would contain tld files.
@ -102,13 +104,24 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra
// and mirroring those in the MANIFEST.MF
Bundle[] bundles = FrameworkUtil.getBundle(PluggableWebAppRegistrationCustomizerImpl.class).getBundleContext().getBundles();
Collection<String> tldbundles = getTldBundles(provider);
HashSet<URL> urls = new HashSet<URL>();
String tmp = System.getProperty(SYS_PROP_TLD_BUNDLES); //comma separated exact names
List<String> sysNames = new ArrayList<String>();
if (tmp != null)
{
StringTokenizer tokenizer = new StringTokenizer(tmp, ", \n\r\t", false);
while (tokenizer.hasMoreTokens())
sysNames.add(tokenizer.nextToken());
}
tmp = (String) deploymentManager.getContextAttribute(OSGiWebInfConfiguration.CONTAINER_BUNDLE_PATTERN); //bundle name patterns
Pattern pattern = (tmp==null? null : Pattern.compile(tmp));
for (Bundle bundle : bundles)
{
if (tldbundles.contains(bundle.getSymbolicName()))
{
if (sysNames.contains(bundle.getSymbolicName()))
registerTldBundle(locatorHelper, bundle, urls);
if (pattern != null && pattern.matcher(bundle.getSymbolicName()).matches())
registerTldBundle(locatorHelper, bundle, urls);
}
}
return urls.toArray(new URL[urls.size()]);
@ -140,7 +153,7 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra
* @param urls
* @throws Exception
*/
private void registerTldBundle(BundleFileLocatorHelper locatorHelper, Bundle bundle, List<URL> urls) throws Exception
private void registerTldBundle(BundleFileLocatorHelper locatorHelper, Bundle bundle, Set<URL> urls) throws Exception
{
File jasperLocation = locatorHelper.getBundleInstallLocation(bundle);
if (jasperLocation.isDirectory())

View File

@ -31,8 +31,8 @@ import javax.servlet.jsp.JspFactory;
import org.apache.jasper.Constants;
import org.apache.jasper.compiler.Localizer;
import org.apache.jasper.xmlparser.ParserUtils;
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.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
import org.eclipse.jetty.util.log.Log;
@ -56,6 +56,7 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
{
private static final Logger LOG = Log.getLogger(WebappRegistrationCustomizerImpl.class);
/**
* Default name of a class that belongs to the jstl bundle. From that class
* we locate the corresponding bundle and register it as a bundle that
@ -90,12 +91,10 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
{
// sanity check:
Class cl = getClass().getClassLoader().loadClass("org.apache.jasper.servlet.JspServlet");
// System.err.println("found the jsp servlet: " + cl.getName());
}
catch (Exception e)
{
System.err.println("Unable to locate the JspServlet: jsp support unavailable.");
e.printStackTrace();
LOG.warn("Unable to locate the JspServlet: jsp support unavailable.", e);
return;
}
try
@ -115,7 +114,7 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
}
catch (Exception e)
{
LOG.warn("Unable to set the JspFactory: jsp support incomplete.",e);
LOG.warn("Unable to set the JspFactory: jsp support incomplete.", e);
}
}
@ -137,7 +136,7 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
* @return array of URLs
* @throws Exception
*/
public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception
public URL[] getJarsWithTlds(DeploymentManager deployer, BundleFileLocatorHelper locatorHelper) throws Exception
{
ArrayList<URL> urls = new ArrayList<URL>();
@ -216,7 +215,7 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
}
catch (Exception e)
{
LOG.warn(e);
e.printStackTrace();
}
}

View File

@ -17,7 +17,9 @@
//
package org.eclipse.jetty.osgi.boot.jsp;
import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleDeployerHelper;
import org.eclipse.jetty.osgi.boot.BundleWebAppProvider;
import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleTrackerCustomizer;
import org.eclipse.jetty.osgi.boot.jasper.PluggableWebAppRegistrationCustomizerImpl;
import org.eclipse.jetty.osgi.boot.jasper.WebappRegistrationCustomizerImpl;
import org.osgi.framework.BundleActivator;
@ -29,7 +31,7 @@ import org.osgi.framework.BundleContext;
* called back by the host bundle.
* <p>
* It must be placed in the org.eclipse.jetty.osgi.boot.jsp package: this is
* because org.eclipse.jetty.osgi.boot.jsp is the sympbolic-name of this
* because org.eclipse.jetty.osgi.boot.jsp is the symbolic-name of this
* fragment. From that name, the PackageadminTracker will call this class. IN a
* different package it won't be called.
* </p>
@ -42,8 +44,11 @@ public class FragmentActivator implements BundleActivator
public void start(BundleContext context) throws Exception
{
System.setProperty("org.apache.jasper.compiler.disablejsr199", Boolean.TRUE.toString());
WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS.add(new WebappRegistrationCustomizerImpl());
WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS.add(new PluggableWebAppRegistrationCustomizerImpl());
WebBundleTrackerCustomizer.JSP_REGISTRATION_HELPERS.add(new WebappRegistrationCustomizerImpl());
WebBundleTrackerCustomizer.JSP_REGISTRATION_HELPERS.add(new PluggableWebAppRegistrationCustomizerImpl());
//Put in the support for the tag libs
addTagLibSupport();
}
/**
@ -53,4 +58,12 @@ public class FragmentActivator implements BundleActivator
{
}
public void addTagLibSupport ()
{
String[] defaultConfigurations = new String[BundleWebAppProvider.getDefaultConfigurations().length+1];
System.arraycopy(BundleWebAppProvider.getDefaultConfigurations(), 0, defaultConfigurations, 0, BundleWebAppProvider.getDefaultConfigurations().length);
defaultConfigurations[defaultConfigurations.length-1] = "org.eclipse.jetty.osgi.boot.jsp.TagLibOSGiConfiguration";
BundleWebAppProvider.setDefaultConfigurations(defaultConfigurations);
}
}

View File

@ -25,6 +25,7 @@ import java.util.Enumeration;
import java.util.LinkedHashSet;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -114,7 +115,7 @@ public class TagLibOSGiConfiguration extends TagLibConfiguration
{
atLeastOneTldFound = true;
URL oriUrl = en.nextElement();
URL url = DefaultFileLocatorHelper.getLocalURL(oriUrl);
URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(oriUrl);
Resource tldResource;
try
{

View File

@ -16,23 +16,6 @@
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
<Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
</Call>
<!-- Providers of OSGi Apps -->
<Call name="addAppProvider">
<Arg>
<New class="org.eclipse.jetty.osgi.boot.OSGiAppProvider">
<!--
<Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
-->
<Set name="scanInterval">0</Set>
<Set name="contextXmlDir"><Property name="jetty.home" default="." />/contexts</Set>
<!-- comma separated list of bundle symbolic names that contain custom tag libraries (*.tld files) -->
<!-- if those bundles don't exist or can't be loaded no errors or warning will be issued! -->
<!-- This default value plugs in the tld files of the reference implementation of JSF -->
<Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldbundles" default="javax.faces.jsf-impl" /></Set>
</New>
</Arg>
</Call>
</New>
</Arg>
</Call>

View File

@ -0,0 +1,206 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.annotations;
import org.eclipse.jetty.annotations.AbstractDiscoverableAnnotationHandler;
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
import org.eclipse.jetty.annotations.ClassNameResolver;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
/**
* Extend the AnnotationConfiguration to support OSGi:
* Look for annotations inside WEB-INF/lib and also in the fragments and required bundles.
* Discover them using a scanner adapted to OSGi instead of the jarscanner.
*/
public class AnnotationConfiguration extends org.eclipse.jetty.annotations.AnnotationConfiguration
{
/**
* This parser scans the bundles using the OSGi APIs instead of assuming a jar.
*/
@Override
protected org.eclipse.jetty.annotations.AnnotationParser createAnnotationParser()
{
return new AnnotationParser();
}
/**
* Here is the order in which jars and osgi artifacts are scanned for discoverable annotations.
* <ol>
* <li>The container jars are scanned.</li>
* <li>The WEB-INF/classes are scanned</li>
* <li>The osgi fragment to the web bundle are parsed.</li>
* <li>The WEB-INF/lib are scanned</li>
* <li>The required bundles are parsed</li>
* </ol>
*/
@Override
public void parseWebInfLib (WebAppContext context, org.eclipse.jetty.annotations.AnnotationParser parser)
throws Exception
{
AnnotationParser oparser = (AnnotationParser)parser;
Bundle webbundle = (Bundle) context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
Bundle[] fragAndRequiredBundles = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(webbundle);
if (fragAndRequiredBundles != null)
{
//index:
for (Bundle bundle : fragAndRequiredBundles)
{
Resource bundleRes = oparser.indexBundle(bundle);
if (!context.getMetaData().getWebInfJars().contains(bundleRes))
{
context.getMetaData().addWebInfJar(bundleRes);
}
}
//scan the fragments
for (Bundle fragmentBundle : fragAndRequiredBundles)
{
if (fragmentBundle.getHeaders().get(Constants.FRAGMENT_HOST) != null)
{
//a fragment indeed:
parseFragmentBundle(context,oparser,webbundle,fragmentBundle);
}
}
}
//scan ourselves
parseWebBundle(context,oparser,webbundle);
//scan the WEB-INF/lib
super.parseWebInfLib(context,parser);
if (fragAndRequiredBundles != null)
{
//scan the required bundles
for (Bundle requiredBundle : fragAndRequiredBundles)
{
if (requiredBundle.getHeaders().get(Constants.FRAGMENT_HOST) == null)
{
//a bundle indeed:
parseRequiredBundle(context,oparser,webbundle,requiredBundle);
}
}
}
}
/**
* Scan a fragment bundle for servlet annotations
* @param context The webapp context
* @param parser The parser
* @param webbundle The current webbundle
* @param fragmentBundle The OSGi fragment bundle to scan
* @throws Exception
*/
protected void parseFragmentBundle(WebAppContext context, AnnotationParser parser,
Bundle webbundle, Bundle fragmentBundle) throws Exception
{
parseBundle(context,parser,webbundle,fragmentBundle);
}
/**
* Scan a bundle required by the webbundle for servlet annotations
* @param context The webapp context
* @param parser The parser
* @param webbundle The current webbundle
* @param fragmentBundle The OSGi required bundle to scan
* @throws Exception
*/
protected void parseWebBundle(WebAppContext context, AnnotationParser parser, Bundle webbundle)
throws Exception
{
parseBundle(context,parser,webbundle,webbundle);
}
/**
* Scan a bundle required by the webbundle for servlet annotations
* @param context The webapp context
* @param parser The parser
* @param webbundle The current webbundle
* @param fragmentBundle The OSGi required bundle to scan
* @throws Exception
*/
protected void parseRequiredBundle(WebAppContext context, AnnotationParser parser,
Bundle webbundle, Bundle requiredBundle) throws Exception
{
parseBundle(context,parser,webbundle,requiredBundle);
}
protected void parseBundle(WebAppContext context, AnnotationParser parser,
Bundle webbundle, Bundle bundle) throws Exception
{
Resource bundleRes = parser.getResource(bundle);
parser.clearHandlers();
for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
{
if (h instanceof AbstractDiscoverableAnnotationHandler)
{
if (webbundle == bundle)
((AbstractDiscoverableAnnotationHandler)h).setResource(null);
else
((AbstractDiscoverableAnnotationHandler)h).setResource(bundleRes);
}
}
parser.registerHandlers(_discoverableAnnotationHandlers);
parser.registerHandler(_classInheritanceHandler);
parser.registerHandlers(_containerInitializerAnnotationHandlers);
parser.parse(bundle,createClassNameResolver(context));
}
/**
* Returns the same classname resolver than for the webInfjar scanner
* @param context
* @return
*/
protected ClassNameResolver createClassNameResolver(final WebAppContext context)
{
return createClassNameResolver(context,true,false,false,false);
}
protected ClassNameResolver createClassNameResolver(final WebAppContext context,
final boolean excludeSysClass, final boolean excludeServerClass, final boolean excludeEverythingElse,
final boolean overrideIsParenLoaderIsPriority)
{
return new ClassNameResolver ()
{
public boolean isExcluded (String name)
{
if (context.isSystemClass(name)) return excludeSysClass;
if (context.isServerClass(name)) return excludeServerClass;
return excludeEverythingElse;
}
public boolean shouldOverride (String name)
{
//looking at system classpath
if (context.isParentLoaderPriority())
return overrideIsParenLoaderIsPriority;
return !overrideIsParenLoaderIsPriority;
}
};
}
}

View File

@ -0,0 +1,197 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.annotations;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import org.eclipse.jetty.annotations.ClassNameResolver;
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.util.resource.Resource;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
/**
*
*/
public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationParser
{
private Set<URI> _alreadyParsed = new HashSet<URI>();
private Map<URI,Bundle> _uriToBundle = new HashMap<URI, Bundle>();
private Map<Bundle,Resource> _resourceToBundle = new HashMap<Bundle,Resource>();
private Map<Bundle,URI> _bundleToUri = new HashMap<Bundle, URI>();
/**
* Keep track of a jetty URI Resource and its associated OSGi bundle.
* @param uri
* @param bundle
* @throws Exception
*/
protected Resource indexBundle(Bundle bundle) throws Exception
{
File bundleFile = BundleFileLocatorHelper.DEFAULT.getBundleInstallLocation(bundle);
Resource resource = Resource.newResource(bundleFile.toURI());
URI uri = resource.getURI();
_uriToBundle.put(uri,bundle);
_bundleToUri.put(bundle,uri);
_resourceToBundle.put(bundle,resource);
return resource;
}
protected URI getURI(Bundle bundle)
{
return _bundleToUri.get(bundle);
}
protected Resource getResource(Bundle bundle)
{
return _resourceToBundle.get(bundle);
}
/**
*
*/
@Override
public void parse (URI[] uris, ClassNameResolver resolver)
throws Exception
{
for (URI uri : uris)
{
Bundle associatedBundle = _uriToBundle.get(uri);
if (associatedBundle == null)
{
if (!_alreadyParsed.add(uri))
{
continue;
}
//a jar in WEB-INF/lib or the WEB-INF/classes
//use the behavior of the super class for a standard jar.
super.parse(new URI[] {uri},resolver);
}
else
{
parse(associatedBundle,resolver);
}
}
}
protected void parse(Bundle bundle, ClassNameResolver resolver)
throws Exception
{
URI uri = _bundleToUri.get(bundle);
if (!_alreadyParsed.add(uri))
{
return;
}
String bundleClasspath = (String)bundle.getHeaders().get(Constants.BUNDLE_CLASSPATH);
if (bundleClasspath == null)
{
bundleClasspath = ".";
}
//order the paths first by the number of tokens in the path second alphabetically.
TreeSet<String> paths = new TreeSet<String>(
new Comparator<String>()
{
public int compare(String o1, String o2)
{
int paths1 = new StringTokenizer(o1,"/",false).countTokens();
int paths2 = new StringTokenizer(o2,"/",false).countTokens();
if (paths1 == paths2)
{
return o1.compareTo(o2);
}
return paths2 - paths1;
}
});
boolean hasDotPath = false;
StringTokenizer tokenizer = new StringTokenizer(bundleClasspath, ",;", false);
while (tokenizer.hasMoreTokens())
{
String token = tokenizer.nextToken().trim();
if (!token.startsWith("/"))
{
token = "/" + token;
}
if (token.equals("/."))
{
hasDotPath = true;
}
else if (!token.endsWith(".jar") && !token.endsWith("/"))
{
paths.add(token+"/");
}
else
{
paths.add(token);
}
}
//support the development environment: maybe the classes are inside bin or target/classes
//this is certainly not useful in production.
//however it makes our life so much easier during development.
if (bundle.getEntry("/.classpath") != null)
{
if (bundle.getEntry("/bin/") != null)
{
paths.add("/bin/");
}
else if (bundle.getEntry("/target/classes/") != null)
{
paths.add("/target/classes/");
}
}
Enumeration classes = bundle.findEntries("/","*.class",true);
if (classes == null)
{
return;
}
while (classes.hasMoreElements())
{
URL classUrl = (URL) classes.nextElement();
String path = classUrl.getPath();
//remove the longest path possible:
String name = null;
for (String prefixPath : paths)
{
if (path.startsWith(prefixPath))
{
name = path.substring(prefixPath.length());
break;
}
}
if (name == null && hasDotPath)
{
//remove the starting '/'
name = path.substring(1);
}
//transform into a classname to pass to the resolver
String shortName = name.replace('/', '.').substring(0,name.length()-6);
if ((resolver == null)|| (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
scanClass(classUrl.openStream());
}
}
}

View File

@ -0,0 +1,291 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import java.util.Dictionary;
import java.util.Hashtable;
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;
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.BundleException;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.BundleTracker;
/**
* Bootstrap jetty and publish a default Server instance as an OSGi service.
*
* Listen for other Server instances to be published as services and support them as deployment targets.
*
* Listen for Bundles to be activated, and deploy those that represent webapps to one of the known Server instances.
*
* <ol>
* <li>basic servlet [ok]</li>
* <li>basic jetty.xml [ok]</li>
* <li>basic jetty.xml and jetty-plus.xml [ok]</li>
* <li>basic jsp [ok]</li>
* <li>jsp with tag-libs [ok]</li>
* <li>test-jndi with atomikos and derby inside ${jetty.home}/lib/ext [ok]</li>
* </ul>
*/
public class JettyBootstrapActivator implements BundleActivator
{
private static JettyBootstrapActivator INSTANCE = null;
public static JettyBootstrapActivator getInstance()
{
return INSTANCE;
}
private ServiceRegistration _registeredServer;
private Server _server;
private JettyContextHandlerServiceTracker _jettyContextHandlerTracker;
private PackageAdminServiceTracker _packageAdminServiceTracker;
private BundleTracker _webBundleTracker;
private BundleContext _bundleContext;
private JettyServerServiceTracker _jettyServerServiceTracker;
/**
* Setup a new jetty Server, registers it as a service. Setup the Service
* tracker for the jetty ContextHandlers that are in charge of deploying the
* webapps. Setup the BundleListener that supports the extender pattern for
* the jetty ContextHandler.
*
* @param context
*/
public void start(BundleContext context) throws Exception
{
INSTANCE = this;
_bundleContext = context;
// track other bundles and fragments attached to this bundle that we
// should activate.
_packageAdminServiceTracker = new PackageAdminServiceTracker(context);
// track Server instances that we should support as deployment targets
_jettyServerServiceTracker = new JettyServerServiceTracker();
context.addServiceListener(_jettyServerServiceTracker, "(objectclass=" + Server.class.getName() + ")");
// track ContextHandler class instances and deploy them to one of the known Servers
_jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(_jettyServerServiceTracker);
context.addServiceListener(_jettyContextHandlerTracker, "(objectclass=" + ContextHandler.class.getName() + ")");
// Create a default jetty instance right now.
DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context);
// track Bundles and deploy those that represent webapps to one of the known Servers
_webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer());
_webBundleTracker.open();
}
/**
* Stop the activator.
*
* @see
* org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception
{
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();
}
catch (IllegalArgumentException ill)
{
// already unregistered.
}
finally
{
_registeredServer = null;
}
}
}
finally
{
if (_server != null)
{
_server.stop();
}
INSTANCE = null;
}
}
/**
* Helper method that creates a new org.jetty.webapp.WebAppContext and
* registers it as an OSGi service. The tracker
* {@link JettyContextHandlerServiceTracker} will do the actual deployment.
*
* @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 "/"
* @throws Exception
*/
public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath) throws Exception
{
checkBundleActivated();
WebAppContext contextHandler = new WebAppContext();
Dictionary<String,String> dic = new Hashtable<String,String>();
dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
String requireTldBundle = (String) contributor.getHeaders().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
if (requireTldBundle != null)
{
dic.put(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE, requireTldBundle);
}
contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
}
/**
* Helper method that creates a new org.jetty.webapp.WebAppContext and
* registers it as an OSGi service. The tracker
* {@link JettyContextHandlerServiceTracker} will do the actual deployment.
*
* @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 dic TODO: parameter description
* @throws Exception
*/
public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath, Dictionary<String, String> dic) throws Exception
{
checkBundleActivated();
WebAppContext contextHandler = new WebAppContext();
dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
}
/**
* Helper method that creates a new skeleton of a ContextHandler and
* registers it as an OSGi service. The tracker
* {@link JettyContextHandlerServiceTracker} will do the actual deployment.
*
* @param contributor The bundle that registers a new context
* @param contextFilePath The path to the file inside the bundle that
* defines the context.
* @throws Exception
*/
public static void registerContext(Bundle contributor, String contextFilePath) throws Exception
{
registerContext(contributor, contextFilePath, new Hashtable<String, String>());
}
/**
* Helper method that creates a new skeleton of a ContextHandler and
* registers it as an OSGi service. The tracker
* {@link JettyContextHandlerServiceTracker} will do the actual deployment.
*
* @param contributor The bundle that registers a new context
* @param contextFilePath The path to the file inside the bundle that
* defines the context.
* @param dic TODO: parameter description
* @throws Exception
*/
public static void registerContext(Bundle contributor, String contextFilePath, Dictionary<String, String> dic) throws Exception
{
checkBundleActivated();
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);
}
public static void unregister(String contextPath)
{
// todo
}
/**
* Since org.eclipse.jetty.osgi.boot does not have a lazy activation policy
* when one of the static methods to register a webapp is called we should
* make sure that the bundle is started.
*/
private static void checkBundleActivated()
{
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;
}
}

View File

@ -0,0 +1,55 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
/**
* 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";
}

View File

@ -0,0 +1,97 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
/**
* Name of the service properties for a ContextHandler that configure a webapp deployed on jetty OSGi.
*/
public class OSGiWebappConstants
{
/** url scheme to deploy war file as bundled webapp */
public static final String RFC66_WAR_URL_SCHEME = "war";
/**
* Name of the header that defines the context path for the embedded webapp.
*/
public static final String RFC66_WEB_CONTEXTPATH = "Web-ContextPath";
/**
* Name of the header that defines the path to the folder where the jsp
* files are extracted.
*/
public static final String RFC66_JSP_EXTRACT_LOCATION = "Jsp-ExtractLocation";
/** Name of the servlet context attribute that points to the bundle context. */
public static final String RFC66_OSGI_BUNDLE_CONTEXT = "osgi-bundlecontext";
/** Name of the servlet context attribute that points to the bundle object.
* We can't always rely on the bundle-context as there might be no such thing. */
public static final String JETTY_OSGI_BUNDLE = "osgi-bundle";
/** List of relative pathes within the bundle to the jetty context files. */
public static final String JETTY_CONTEXT_FILE_PATH = "Jetty-ContextFilePath";
/** path within the bundle to the folder that contains the basic resources. */
public static final String JETTY_WAR_FOLDER_PATH = "Jetty-WarFolderPath";
/** path within a fragment hosted by a web-bundle to a folder that contains basic resources.
* the path is appended to the lookup path where jetty locates static resources */
public static final String JETTY_WAR_FRAGMENT_FOLDER_PATH = "Jetty-WarFragmentFolderPath";
/** path within a fragment hosted by a web-bundle to a folder that contains basic resources.
* The path is prefixed to the lookup path where jetty locates static resources:
* this will override static resources with the same name in the web-bundle. */
public static final String JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH = "Jetty-WarPatchFragmentFolderPath";
// OSGi ContextHandler service properties.
/** web app context path */
public static final String SERVICE_PROP_CONTEXT_PATH = "contextPath";
/** Path to the web application base folder */
public static final String SERVICE_PROP_WAR = "war";
/** Extra classpath */
public static final String SERVICE_PROP_EXTRA_CLASSPATH = "extraClasspath";
/** jetty context file path */
public static final String SERVICE_PROP_CONTEXT_FILE_PATH = "contextFilePath";
/** web.xml file path */
public static final String SERVICE_PROP_WEB_XML_PATH = "webXmlFilePath";
/** defaultweb.xml file path */
public static final String SERVICE_PROP_DEFAULT_WEB_XML_PATH = "defaultWebXmlFilePath";
/**
* path to the base folder that overrides the computed bundle installation
* 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";
/**
* Comma separated list of bundles that contain tld file used by the webapp.
*/
public static final String REQUIRE_TLD_BUNDLE = "Require-TldBundle";
/**
* Comma separated list of bundles that contain tld file used by the webapp.
* Both the name of the manifest header and the name of the service property.
*/
public static final String SERVICE_PROP_REQUIRE_TLD_BUNDLE = REQUIRE_TLD_BUNDLE;
}

View File

@ -0,0 +1,65 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.jsp;
import java.net.URL;
import java.net.URLClassLoader;
/**
* Tricky url classloader. In fact we don't want a real URLClassLoader: we want
* OSGi to provide its classloader and let it does. But to let
* {@link org.apache.jasper.compiler.TldLocationsCache} find the core tlds
* inside the jars we must be a URLClassLoader that returns an array of jars
* where tlds are stored when the method getURLs is called.
*/
public class TldLocatableURLClassloader extends URLClassLoader
{
private URL[] _jarsWithTldsInside;
public TldLocatableURLClassloader(ClassLoader osgiClassLoader, URL[] jarsWithTldsInside)
{
super(new URL[] {},osgiClassLoader);
_jarsWithTldsInside = jarsWithTldsInside;
}
/**
* @return the jars that contains tlds so that TldLocationsCache or
* TldScanner can find them.
*/
@Override
public URL[] getURLs()
{
return _jarsWithTldsInside;
}
public String toString()
{
StringBuilder builder = new StringBuilder();
if (_jarsWithTldsInside != null)
{
for (URL u:_jarsWithTldsInside)
builder.append(" "+u.toString());
return builder.toString();
}
else
return super.toString();
}
}

View File

@ -0,0 +1,69 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.jsp;
import java.net.URL;
/**
* Add a classloader to the
* org.apache.jasper.compiler.TldLocatableURLClassloader. Hopefuly not
* necessary: still experimenting.
*
* @see TldLocatableURLClassloader
*/
public class TldLocatableURLClassloaderWithInsertedJettyClassloader extends TldLocatableURLClassloader
{
private ClassLoader _internalClassLoader;
/**
*
* @param osgiClassLoaderParent
* The parent classloader
* @param internalClassLoader
* The classloader that will be at the same level than the
* jarsWithTldsInside
* @param jarsWithTldsInside
* jars that are scanned for tld files.
*/
public TldLocatableURLClassloaderWithInsertedJettyClassloader(ClassLoader osgiClassLoaderParent, ClassLoader internalClassLoader, URL[] jarsWithTldsInside)
{
super(osgiClassLoaderParent,jarsWithTldsInside);
_internalClassLoader = internalClassLoader;
}
protected Class<?> findClass(String name) throws ClassNotFoundException
{
try
{
return super.findClass(name);
}
catch (ClassNotFoundException cne)
{
if (_internalClassLoader != null)
{
return _internalClassLoader.loadClass(name);
}
else
{
throw cne;
}
}
}
}

View File

@ -0,0 +1,338 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.net.MalformedURLException;
import java.net.URL;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
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.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
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.
*/
public class DefaultJettyAtJettyHomeHelper
{
private static final Logger LOG = Log.getLogger(DefaultJettyAtJettyHomeHelper.class);
/**
* 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";
/**
* Set of config files to apply to a jetty Server instance if none are supplied by SYS_PROP_JETTY_ETC_FILES
*/
public static final String DEFAULT_JETTY_ETC_FILES = "jetty.xml,jetty-selector.xml,jetty-deployer.xml";
/**
* Default location within bundle of a jetty home dir.
*/
public static final String DEFAULT_JETTYHOME = "/jettyhome";
/**
* Called by the JettyBootStrapActivator. If the system property jetty.home
* is defined and points to a folder, creates a 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.
* </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 separated
* list of URLs or relative paths inside the bundle or folder to the config
* files. If undefined it defaults to 'etc/jetty.xml'. In the case of the jetty.home.bundle,
* if no etc/jetty.xml file is found in the bundle, it will look for
* /jettyhome/etc/jetty-osgi-default.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) throws Exception
{
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)
{
jettyHomeSysProp = resolvePropertyValue(jettyHomeSysProp);
// bug 329621
if (jettyHomeSysProp.startsWith("\"") && jettyHomeSysProp.endsWith("\"") || (jettyHomeSysProp.startsWith("'") && jettyHomeSysProp.endsWith("'")))
{
jettyHomeSysProp = jettyHomeSysProp.substring(1, jettyHomeSysProp.length() - 1);
}
if (jettyHomeBundleSysProp != null)
{
LOG.warn("Both jetty.home and jetty.home.bundle property defined: jetty.home.bundle ignored.");
}
jettyHome = new File(jettyHomeSysProp);
if (!jettyHome.exists() || !jettyHome.isDirectory())
{
LOG.warn("Unable to locate the jetty.home folder " + jettyHomeSysProp);
return;
}
}
else if (jettyHomeBundleSysProp != null)
{
jettyHomeBundleSysProp = resolvePropertyValue(jettyHomeBundleSysProp);
for (Bundle b : bundleContext.getBundles())
{
if (b.getSymbolicName().equals(jettyHomeBundleSysProp))
{
jettyHomeBundle = b;
break;
}
}
if (jettyHomeBundle == null)
{
LOG.warn("Unable to find the jetty.home.bundle named " + jettyHomeSysProp);
return;
}
}
if (jettyHome == null && jettyHomeBundle == null)
{
LOG.warn("No default jetty created.");
return;
}
Server server = new Server();
Dictionary<String,String> properties = new Hashtable<String,String>();
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);
LOG.info("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));
//register the Server instance as an OSGi service.
bundleContext.registerService(Server.class.getName(), server, properties);
// hookNestedConnectorToBridgeServlet(server);
}
/**
* 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)
{
LOG.warn(e);
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 files = System.getProperty(SYS_PROP_JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
StringTokenizer tokenizer = new StringTokenizer(files, ";,", false);
StringBuilder res = new StringBuilder();
while (tokenizer.hasMoreTokens())
{
String etcFile = tokenizer.nextToken().trim();
if (etcFile.startsWith("/") || etcFile.indexOf(":") != -1)
{
//file path is absolute
appendToCommaSeparatedList(res, etcFile);
}
else
{
//relative file path
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()))
{
String tmp = DEFAULT_JETTYHOME+"/etc/"+etcFile;
enUrls = BundleFileLocatorHelper.DEFAULT.findEntries(configurationBundle, tmp);
LOG.info("Configuring jetty with the default embedded configuration:" + "bundle: "
+ configurationBundle.getSymbolicName()
+ " config: "+tmp);
}
if (enUrls == null || !enUrls.hasMoreElements())
{
LOG.warn("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(Dictionary<String,String> properties, String key, String value)
{
if (value != null)
{
properties.put(key, value);
}
}
/**
* recursively substitute the ${sysprop} by their actual system property.
* ${sysprop,defaultvalue} will use 'defaultvalue' as the value if no
* sysprop is defined. Not the most efficient code but we are shooting for
* simplicity and speed of development here.
*
* @param value
* @return
*/
public static String resolvePropertyValue(String value)
{
int ind = value.indexOf("${");
if (ind == -1) { return value; }
int ind2 = value.indexOf('}', ind);
if (ind2 == -1) { return value; }
String sysprop = value.substring(ind + 2, ind2);
String defaultValue = null;
int comma = sysprop.indexOf(',');
if (comma != -1 && comma + 1 != sysprop.length())
{
defaultValue = sysprop.substring(comma + 1);
defaultValue = resolvePropertyValue(defaultValue);
sysprop = sysprop.substring(0, comma);
}
else
{
defaultValue = "${" + sysprop + "}";
}
String v = System.getProperty(sysprop);
String reminder = value.length() > ind2 + 1 ? value.substring(ind2 + 1) : "";
reminder = resolvePropertyValue(reminder);
if (v != null)
{
return value.substring(0, ind) + v + reminder;
}
else
{
return value.substring(0, ind) + defaultValue + reminder;
}
}
}

View File

@ -0,0 +1,34 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
/**
* 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);
}

View File

@ -0,0 +1,172 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
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
{
private static Logger LOG = Log.getLogger(JettyServerServiceTracker.class.getName());
/**
* 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)
{
LOG.warn(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)
{
LOG.warn(e);
}
}
if (ev.getType() == ServiceEvent.UNREGISTERING)
{
break;
}
else
{
// modified, meaning: we reload it. now that we stopped it;
// we can register it.
}
}
case ServiceEvent.REGISTERED:
{
try
{
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);
}
catch (Exception e)
{
LOG.warn(e);
}
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);
}
}

View File

@ -0,0 +1,444 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.URL;
import java.util.ArrayList;
import java.util.Collection;
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;
/**
* ServerInstanceWrapper
*
* Configures and starts a jetty Server instance.
*/
public class ServerInstanceWrapper
{
/**
* The value of this property points to the parent director of the jetty.xml
* configuration file currently executed. Everything is passed as a URL to
* support the case where the bundle is zipped.
*/
public static final String PROPERTY_THIS_JETTY_XML_FOLDER_URL = "this.jetty.xml.parent.folder.url";
private static Logger LOG = 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) throws Exception
{
_server = server;
ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
try
{
// passing this bundle's classloader as the context classloader
// 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);
List<File> shared = sharedURLs != null ? extractFiles(sharedURLs) : null;
libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader(shared, null, server, JettyBootstrapActivator.class.getClassLoader());
Thread.currentThread().setContextClassLoader(libExtClassLoader);
configure(server, props);
init();
// now that we have an app provider we can call the registration
// customizer.
URL[] jarsWithTlds = getJarsWithTlds();
_commonParentClassLoaderForWebapps = jarsWithTlds == null ? libExtClassLoader : new TldLocatableURLClassloader(libExtClassLoader, jarsWithTlds);
server.start();
_webBundleDeployerHelper = new WebBundleDeployerHelper(this);
}
catch (Exception e)
{
if (server != null)
{
try
{
server.stop();
}
catch (Exception x)
{
LOG.ignore(x);
}
}
throw e;
}
finally
{
Thread.currentThread().setContextClassLoader(contextCl);
}
}
public void stop()
{
try
{
if (_server.isRunning())
{
_server.stop();
}
}
catch (Exception e)
{
LOG.warn(e);
}
}
/**
* 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 implementation.
*
* 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 development 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<String, Object> id_map = new HashMap<String, Object>();
//TODO need to put in the id of the server being configured
id_map.put("Server", server);
Map<String, String> properties = new HashMap<String, String>();
Enumeration<Object> en = props.keys();
while (en.hasMoreElements())
{
Object key = en.nextElement();
Object value = props.get(key);
properties.put(String.valueOf(key), String.valueOf(value));
}
for (URL jettyConfiguration : jettyConfigurations)
{
InputStream is = null;
try
{
// Execute a Jetty configuration file
Resource r = Resource.newResource(jettyConfiguration);
is = r.getInputStream();
XmlConfiguration config = new XmlConfiguration(is);
config.getIdMap().putAll(id_map);
// #334062 compute the URL of the folder that contains the
// jetty.xml conf file
// and set it as a property so we can compute relative paths
// from it.
String urlPath = jettyConfiguration.toString();
int lastSlash = urlPath.lastIndexOf('/');
if (lastSlash > 4)
{
urlPath = urlPath.substring(0, lastSlash);
Map<String, String> properties2 = new HashMap<String, String>(properties);
properties2.put(PROPERTY_THIS_JETTY_XML_FOLDER_URL, urlPath);
config.getProperties().putAll(properties2);
}
else
{
config.getProperties().putAll(properties);
}
config.configure();
id_map = config.getIdMap();
}
catch (SAXParseException saxparse)
{
LOG.warn("Unable to configure the jetty/etc file " + jettyConfiguration, saxparse);
throw saxparse;
}
finally
{
IO.close(is);
}
}
}
/**
* Must be called after the server is configured.
*
* It is assumed the server has already been configured with the ContextHandlerCollection structure.
*
* The server must have an instance of OSGiAppProvider. If one is not provided, it is created.
*/
private void init()
{
// Get the context handler
_ctxtHandler = (ContextHandlerCollection) _server.getChildHandlerByClass(ContextHandlerCollection.class);
// get a deployerManager
Collection<DeploymentManager> deployers = _server.getBeans(DeploymentManager.class);
if (deployers != null && !deployers.isEmpty())
{
_deploymentManager = deployers.iterator().next();
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.setMonitoredDirResource(Resource.newResource(getDefaultOSGiContextsHome(new File(System.getProperty("jetty.home"))).toURI()));
}
catch (IOException e)
{
LOG.warn(e);
}
_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)
{
LOG.warn(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)
{
LOG.warn(mfe);
}
}
return files;
}
}

View File

@ -0,0 +1,87 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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 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 requireTldBundle The list of bundles's symbolic names that contain
* tld files that are required by this WAB.
* @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 requireTldBundle, 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 requireTldBundle The list of bundles'symbolic name that contain
* tld files for this webapp.
* @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, String requireTldBundle, ContextHandler handler) throws Exception;
}

View File

@ -0,0 +1,390 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.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.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper;
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.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
/**
* When a {@link ContextHandler} service is activated we look into it and if the
* corresponding webapp is actually not configured then we go and register it.
* <p>
* The idea is to always go through this class when we deploy a new webapp on
* jetty.
* </p>
* <p>
* We are exposing each web-application as an OSGi service. This lets us update
* the webapps and stop/start them directly at the OSGi layer. It also give us
* many ways to declare those services: Declarative Services for example. <br/>
* It is a bit different from the way the HttpService works where we would have
* a WebappService and we woud register a webapp onto it. <br/>
* It does not go against RFC-66 nor does it prevent us from supporting the
* WebappContainer semantics.
* </p>
*/
public class JettyContextHandlerServiceTracker implements ServiceListener
{
private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName());
/** 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/path/to/context/file when there is
* such thing
*/
private Map<String, ServiceReference> _indexByContextFile = new HashMap<String, ServiceReference>();
/** in charge of detecting changes in the osgi contexts home folder. */
private Scanner _scanner;
/**
* @param registry
*/
public JettyContextHandlerServiceTracker(IManagedJettyServerRegistry registry) throws Exception
{
_registry = registry;
}
public void stop() throws Exception
{
if (_scanner != null)
{
_scanner.stop();
}
// the class that created the server is also in charge of stopping it.
// 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.
*
* @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:
{
ContextHandler ctxtHandler = unregisterInIndex(ev.getServiceReference());
if (ctxtHandler != null && !ctxtHandler.isStopped())
{
try
{
getWebBundleDeployerHelp(sr).unregister(ctxtHandler);
}
catch (Exception e)
{
__logger.warn(e);
}
}
}
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();
BundleContext context = FrameworkUtil.getBundle(JettyBootstrapActivator.class).getBundleContext();
ContextHandler contextHandler = (ContextHandler) context.getService(sr);
if (contextHandler.getServer() != null)
{
// is configured elsewhere.
return;
}
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);
if (contextPath == null)
{
contextPath = webapp.getContextPath();
}
String webXmlPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WEB_XML_PATH);
if (webXmlPath == null)
{
webXmlPath = webapp.getDescriptor();
}
String defaultWebXmlPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_DEFAULT_WEB_XML_PATH);
if (defaultWebXmlPath == null)
{
String jettyHome = System.getProperty(DefaultJettyAtJettyHomeHelper.SYS_PROP_JETTY_HOME);
if (jettyHome != null)
{
File etc = new File(jettyHome, "etc");
if (etc.exists() && etc.isDirectory())
{
File webDefault = new File(etc, "webdefault.xml");
if (webDefault.exists())
defaultWebXmlPath = webDefault.getAbsolutePath();
else
defaultWebXmlPath = webapp.getDefaultsDescriptor();
}
else
defaultWebXmlPath = webapp.getDefaultsDescriptor();
}
}
String war = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WAR);
try
{
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),
(String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE),
webXmlPath, defaultWebXmlPath, webapp);
if (handler != null)
{
registerInIndex(handler, sr);
}
}
}
catch (Throwable e)
{
__logger.warn(e);
}
}
else
{
// consider this just an empty skeleton:
if (contextFilePath == null) { throw new IllegalArgumentException("the property contextFilePath is required"); }
try
{
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),
(String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE),
contextHandler);
if (handler != null)
{
registerInIndex(handler, sr);
}
}
}
catch (Throwable e)
{
__logger.warn(e);
}
}
}
break;
}
}
private void registerInIndex(ContextHandler handler, ServiceReference sr)
{
_indexByServiceReference.put(sr, handler);
String key = getSymbolicNameAndContextFileKey(sr);
if (key != null)
{
_indexByContextFile.put(key, sr);
}
}
/**
* Returns the ContextHandler to stop.
*
* @param reg
* @return the ContextHandler to stop.
*/
private ContextHandler unregisterInIndex(ServiceReference sr)
{
ContextHandler handler = _indexByServiceReference.remove(sr);
String key = getSymbolicNameAndContextFileKey(sr);
if (key != null)
{
_indexByContextFile.remove(key);
}
if (handler == null)
{
// a warning?
return null;
}
return handler;
}
/**
* @param sr
* @return The key for a context file within the osgi contexts home folder.
*/
private String getSymbolicNameAndContextFileKey(ServiceReference sr)
{
String contextFilePath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
if (contextFilePath != null) { return sr.getBundle().getSymbolicName() + "/" + contextFilePath; }
return null;
}
/**
* Called by the scanner when one of the context files is changed.
*
* @param contextFileFully
*/
public void reloadJettyContextHandler(String canonicalNameOfFileChanged, String osgiContextHomeFolderCanonicalPath)
{
String key = getNormalizedRelativePath(canonicalNameOfFileChanged, osgiContextHomeFolderCanonicalPath);
if (key == null) { return; }
ServiceReference sr = _indexByContextFile.get(key);
if (sr == null)
{
// nothing to do?
return;
}
serviceChanged(new ServiceEvent(ServiceEvent.MODIFIED, sr));
}
/**
* @param canFilename
* @return
*/
private String getNormalizedRelativePath(String canFilename, String 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 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;
}
}

View File

@ -0,0 +1,213 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.eclipse.jetty.server.Server;
/**
* Helper to create a URL class-loader with the jars inside
* ${jetty.home}/lib/ext and ${jetty.home}/resources. In an ideal world, every
* library is an OSGi bundle that does loads nicely. To support standard jars or
* bundles that cannot be loaded in the current OSGi environment, we support
* inserting the jars in the usual jetty/lib/ext folders in the proper classpath
* for the webapps.
* <p>
* Also the folder resources typically contains central configuration files for
* things like: log config and others. We enable fragments to register classes
* that are called back and passed those resources to do what they need to do.
* </p>
* <p>
* For example the test-jndi webapplication depends on derby, derbytools,
* atomikos none of them are osgi bundles. we can either re-package them or we
* can place them in the usual lib/ext. <br/>
* In fact jasper's jsp libraries should maybe place in lib/ext too.
* </p>
* <p>
* The drawback is that those libraries will not be available in the OSGi
* classloader. Note that we could have setup those jars as embedded jars of the
* current bundle. However, we would need to know in advance what are those jars
* which was not acceptable. Also having those jars in a URLClassLoader seem to
* be required for some cases. For example jaspers' TldLocationsCache (replaced
* by TldScanner for servlet-3.0). <br/>
* Also all the dependencies of those libraries must be resolvable directly from
* the JettyBootstrapActivator bundle as it is set as the parent classloader. For
* example: if atomikos is placed in lib/ext it will work if and only if
* JettyBootstrapActivator import the necessary packages from javax.naming*,
* javax.transaction*, javax.mail* etc Most of the common cases of javax are
* added as optional import packages into jetty bootstrapper plugin. When there
* are not covered: please make a request or create a fragment or register a
* bundle with a buddy-policy onto the jetty bootstrapper..
* </p>
* <p>
* Alternatives to placing jars in lib/ext
* <ol>
* <li>Bundle the jars in an osgi bundle. Have the webapp(s) that context
* depends on them depend on that bundle. Things will go well for jetty.</li>
* <li>Bundle those jars in an osgi bundle-fragment that targets the
* jetty-bootstrap bundle</li>
* <li>Use equinox Buddy-Policy: register a buddy of the jetty bootstrapper
* bundle. (least favorite: it will work only on equinox)</li>
* </ol>
* </p>
*/
public class LibExtClassLoaderHelper
{
/**
* Class called back
*/
public interface IFilesInJettyHomeResourcesProcessor
{
void processFilesInResourcesFolder(File jettyHome, Map<String, File> filesInResourcesFolder);
}
public static Set<IFilesInJettyHomeResourcesProcessor> registeredFilesInJettyHomeResourcesProcessors = new HashSet<IFilesInJettyHomeResourcesProcessor>();
/**
* @param server
* @return a url classloader with the jars of resources, lib/ext and the
* jars passed in the other argument. The parent classloader usually
* is the JettyBootStrapper (an osgi classloader.
* @throws MalformedURLException
*/
public static 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())
{
// make sure it contains something else than README:
Map<String, File> jettyResFiles = new HashMap<String, File>();
for (File f : jettyResources.listFiles())
{
jettyResFiles.put(f.getName(), f);
if (f.getName().toLowerCase(Locale.ENGLISH).startsWith("readme"))
{
continue;
}
else
{
if (urls.isEmpty())
{
urls.add(jettyResources.toURI().toURL());
}
}
}
processFilesInResourcesFolder(jettyHome, jettyResFiles);
}
File libExt = new File(jettyHome, "lib/ext");
if (libExt.exists())
{
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);
}
/**
* @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.
* <p>
* We can afford to do some implementation specific code for a logging
* framework only in a fragment. <br/>
* Trying to configure log4j and logback in here.
* </p>
* <p>
* We recommend that slf4j jars are all placed in the osgi framework. And a
* single implementation if possible packaged as an osgi bundle is there.
* </p>
*/
protected static void processFilesInResourcesFolder(File jettyHome, Map<String, File> childrenFiles)
{
for (IFilesInJettyHomeResourcesProcessor processor : registeredFilesInJettyHomeResourcesProcessors)
{
processor.processFilesInResourcesFolder(jettyHome, childrenFiles);
}
}
}

View File

@ -0,0 +1,285 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
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 implements BundleReference
{
private Logger __logger = Log.getLogger(OSGiWebappClassLoader.class.getName().toString());
/**
* when a logging framework is setup in the osgi classloaders, it can access
* this and register the classes that must not be found in the jar.
*/
public static Set<String> JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED = new HashSet<String>();
public static void addClassThatIdentifiesAJarThatMustBeRejected(Class<?> zclass)
{
JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(zclass.getName().replace('.', '/') + ".class");
}
public static void addClassThatIdentifiesAJarThatMustBeRejected(String zclassName)
{
JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(zclassName.replace('.', '/') + ".class");
}
static
{
addClassThatIdentifiesAJarThatMustBeRejected(HttpServlet.class);
}
private ClassLoader _osgiBundleClassLoader;
private Bundle _contributor;
private boolean _lookInOsgiFirst = true;
private Set<String> _libsAlreadyInManifest = new HashSet<String>();
/**
* @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);
_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
* libs we should not add them to the classpath of the webapp. Not really
* important as we resolve classes through the osgi classloader first and
* then default on the libs of the webapp.
*/
private void computeLibsAlreadyInOSGiClassLoader()
{
// TODO
}
@Override
public Enumeration<URL> getResources(String name) throws IOException
{
Enumeration<URL> osgiUrls = _osgiBundleClassLoader.getResources(name);
Enumeration<URL> urls = super.getResources(name);
if (_lookInOsgiFirst)
{
return Collections.enumeration(toList(osgiUrls, urls));
}
else
{
return Collections.enumeration(toList(urls, osgiUrls));
}
}
@Override
public URL getResource(String name)
{
if (_lookInOsgiFirst)
{
URL url = _osgiBundleClassLoader.getResource(name);
return url != null ? url : super.getResource(name);
}
else
{
URL url = super.getResource(name);
return url != null ? url : _osgiBundleClassLoader.getResource(name);
}
}
private List<URL> toList(Enumeration<URL> e, Enumeration<URL> e2)
{
List<URL> list = new ArrayList<URL>();
while (e != null && e.hasMoreElements())
list.add(e.nextElement());
while (e2 != null && e2.hasMoreElements())
list.add(e2.nextElement());
return list;
}
/**
*
*/
protected Class<?> findClass(String name) throws ClassNotFoundException
{
try
{
return _lookInOsgiFirst ? _osgiBundleClassLoader.loadClass(name) : super.findClass(name);
}
catch (ClassNotFoundException cne)
{
try
{
return _lookInOsgiFirst ? super.findClass(name) : _osgiBundleClassLoader.loadClass(name);
}
catch (ClassNotFoundException cne2)
{
throw cne;
}
}
}
/**
* Parse the classpath ourselves to be able to filter things. This is a
* derivative work of the super class
*/
@Override
public void addClassPath(String classPath) throws IOException
{
StringTokenizer tokenizer = new StringTokenizer(classPath, ",;");
while (tokenizer.hasMoreTokens())
{
String path = tokenizer.nextToken();
Resource resource = getContext().newResource(path);
// Resolve file path if possible
File file = resource.getFile();
if (file != null && isAcceptableLibrary(file, JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED))
{
super.addClassPath(path);
}
else
{
__logger.info("Did not add " + path + " to the classloader of the webapp " + getContext());
}
}
}
/**
* @param lib
* @return true if the lib should be included in the webapp classloader.
*/
private boolean isAcceptableLibrary(File file, Set<String> pathToClassFiles)
{
try
{
if (file.isDirectory())
{
for (String criteria : pathToClassFiles)
{
if (new File(file, criteria).exists()) { return false; }
}
}
else
{
JarFile jar = null;
try
{
jar = new JarFile(file);
for (String criteria : pathToClassFiles)
{
if (jar.getEntry(criteria) != null) { return false; }
}
}
finally
{
if (jar != null) try
{
jar.close();
}
catch (IOException ioe)
{
}
}
}
}
catch (IOException e)
{
// nevermind. just trying our best
__logger.ignore(e);
}
return true;
}
private static Field _contextField;
/**
* In the case of the generation of a webapp via a jetty context file we
* need a proper classloader to setup the app before we have the
* WebappContext So we place a fake one there to start with. We replace it
* with the actual webapp context with this method. We also apply the
* extraclasspath there at the same time.
*/
public void setWebappContext(WebAppContext webappContext)
{
try
{
if (_contextField == null)
{
_contextField = WebAppClassLoader.class.getDeclaredField("_context");
_contextField.setAccessible(true);
}
_contextField.set(this, webappContext);
if (webappContext.getExtraClasspath() != null)
{
addClassPath(webappContext.getExtraClasspath());
}
}
catch (Throwable t)
{
// humf that will hurt if it does not work.
__logger.warn("Unable to set webappcontext", t);
}
}
}

View File

@ -0,0 +1,271 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.net.URL;
import java.util.Dictionary;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.osgi.util.tracker.BundleTracker;
import org.osgi.util.tracker.BundleTrackerCustomizer;
/**
* Support bundles that declare the webapp directly through headers in their
* manifest.
* <p>
* Those headers will define a new WebApplication:
* <ul>
* <li>Web-ContextPath</li>
* <li>Jetty-WarFolderPath</li>
* </ul>
* </p>
* <p>
* Those headers will define a new app started via a jetty-context or a list of
* them. ',' column is the separator between the various context files.
* <ul>
* <li>Jetty-ContextFilePath</li>
* </ul>
* </p>
* And generate a jetty WebAppContext or another ContextHandler then registers
* it as service. Kind of simpler than declarative services and their xml files.
* Also avoid having the contributing bundle depend on jetty's package for
* WebApp.
*
* @author hmalphettes
*/
public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
{
private static final Logger LOG = Log.getLogger(WebBundleTrackerCustomizer.class);
/**
* 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 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.
if (bundle.getState() == Bundle.STOPPING || bundle.getState() == Bundle.ACTIVE)
{
unregister(bundle);
}
if (bundle.getState() == Bundle.ACTIVE)
{
register(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);
if (warFolderRelativePath != null)
{
String contextPath = getWebContextPath(bundle, dic, false);
if (contextPath == null || !contextPath.startsWith("/"))
{
LOG.warn("The manifest header '" + OSGiWebappConstants.JETTY_WAR_FOLDER_PATH
+ ": "
+ warFolderRelativePath
+ "' in the bundle "
+ bundle.getSymbolicName()
+ " is not valid: there is no Web-ContextPath defined in the manifest.");
return false;
}
// create the corresponding service and publish it in the context of
// the contributor bundle.
try
{
JettyBootstrapActivator.registerWebapplication(bundle, warFolderRelativePath, contextPath);
return true;
}
catch (Throwable e)
{
LOG.warn("Starting the web-bundle " + bundle.getSymbolicName() + " threw an exception.", e);
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)
{
String contextFileRelativePath = (String) dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
if (contextFileRelativePath == null)
{
// nothing to register here.
return false;
}
// support for multiple webapps in the same bundle:
String[] pathes = contextFileRelativePath.split(",;");
for (String path : pathes)
{
try
{
JettyBootstrapActivator.registerContext(bundle, path.trim());
}
catch (Throwable e)
{
LOG.warn(e);
}
}
return true;
}
else
{
// support for OSGi-RFC66; disclaimer, no access to the actual
// (draft) of the spec: just a couple of posts on the
// world-wide-web.
URL rfc66Webxml = bundle.getEntry("/WEB-INF/web.xml");
if (rfc66Webxml == null && dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) == null)
{
return false;// no webapp in here
}
// this is risky: should we make sure that there is no classes and
// jars directly available
// at the root of the of the bundle: otherwise they are accessible
// through the browser. we should enforce that the whole classpath
// is
// pointing to files and folders inside WEB-INF. We should
// filter-out
// META-INF too
String rfc66ContextPath = getWebContextPath(bundle, dic, rfc66Webxml == null);
try
{
JettyBootstrapActivator.registerWebapplication(bundle, ".", rfc66ContextPath);
return true;
}
catch (Throwable e)
{
LOG.warn(e);
return true;// maybe it did not work maybe it did. safer to track this bundle.
}
}
}
private String getWebContextPath(Bundle bundle, Dictionary<?, ?> dic, boolean webinfWebxmlExists)
{
String rfc66ContextPath = (String) dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (rfc66ContextPath == null)
{
if (!webinfWebxmlExists) { return null; }
// extract from the last token of the bundle's location:
// (really ?
// could consider processing the symbolic name as an alternative
// the location will often reflect the version.
// maybe this is relevant when the file is a war)
String location = bundle.getLocation();
String toks[] = location.replace('\\', '/').split("/");
rfc66ContextPath = toks[toks.length - 1];
// remove .jar, .war etc:
int lastDot = rfc66ContextPath.lastIndexOf('.');
if (lastDot != -1)
{
rfc66ContextPath = rfc66ContextPath.substring(0, lastDot);
}
}
if (!rfc66ContextPath.startsWith("/"))
{
rfc66ContextPath = "/" + rfc66ContextPath;
}
return rfc66ContextPath;
}
private void unregister(Bundle bundle)
{
// nothing to do: when the bundle is stopped, each one of its service
// reference is also stopped and that is what we use to stop the
// corresponding
// webapps registered in that bundle.
}
}

View File

@ -0,0 +1,54 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.utils;
import org.eclipse.jetty.osgi.boot.utils.internal.DefaultBundleClassLoaderHelper;
import org.osgi.framework.Bundle;
/**
* Is there a clean OSGi way to go from the Bundle object to the classloader of
* the Bundle ? You can certainly take a class inside the bundle and get the
* bundle's classloader that way. Getting the classloader directly from the
* bundle would be nice.
* <p>
* We could use fragments that are specific to each OSGi implementation. Using
* introspection here to keep packaging simple and avoid the multiplication of
* the jars.
* </p>
* <p>
* The default implementation relies on introspection and supports equinox-3.5
* and felix-2.0.0
* </p>
*/
public interface BundleClassLoaderHelper
{
/** The name of the custom implementation for this interface in a fragment. */
public static final String CLASS_NAME = "org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelperImpl";
/** The default instance supports felix and equinox */
public static BundleClassLoaderHelper DEFAULT = new DefaultBundleClassLoaderHelper();
/**
* @return The classloader of a given bundle. Assuming the bundle is
* started.
*/
public ClassLoader getBundleClassLoader(Bundle bundle);
}

View File

@ -0,0 +1,93 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.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;
/**
* From a bundle to its location on the filesystem. Assumes the bundle is not a
* jar.
*
* @author hmalphettes
*/
public interface BundleFileLocatorHelper
{
/** The name of the custom implementation for this interface in a fragment. */
public static final String CLASS_NAME = "org.eclipse.jetty.osgi.boot.utils.FileLocatorHelperImpl";
/** The default instance supports felix and equinox */
public static BundleFileLocatorHelper DEFAULT = new DefaultFileLocatorHelper();
/**
* Works with equinox, felix, nuxeo and probably more. Not exactly in the
* spirit of OSGi but quite necessary to support self-contained webapps and
* other situations.
* <p>
* Currently only works with bundles that are not jar.
* </p>
*
* @param bundle The bundle
* @return Its installation location as a file.
* @throws Exception
*/
public File getBundleInstallLocation(Bundle bundle) throws Exception;
/**
* Locate a file inside a bundle.
*
* @param bundle
* @param path
* @return file object
* @throws Exception
*/
public File getFileInBundle(Bundle bundle, String path) throws Exception;
/**
* If the bundle is a jar, returns the jar. If the bundle is a folder, look
* inside it and search for jars that it returns.
* <p>
* Good enough for our purpose (TldLocationsCache when it scans for tld
* files inside jars alone. In fact we only support the second situation for
* development purpose where the bundle was imported in pde and the classes
* kept in a jar.
* </p>
*
* @param bundle
* @return The jar(s) file that is either the bundle itself, either the jars
* 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);
}

View File

@ -0,0 +1,60 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.utils;
import java.net.URL;
import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
/**
* Fix various shortcomings with the way jasper parses the tld files.
*/
public interface WebappRegistrationCustomizer
{
/**
* we could do something a lot more pluggable with a custom header in the
* manifest or some customer declarative services let's keep it simple for
* now. hopefully the rest of the world won't need to customize this.
*/
public static final String CLASS_NAME = "org.eclipse.jetty.osgi.boot.jasper.WebappRegistrationCustomizerImpl";
/**
* 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 array of URLs
* @throws Exception
*/
URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper fileLocator) throws Exception;
}

View File

@ -0,0 +1,242 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.utils.internal;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelper;
import org.osgi.framework.Bundle;
/**
* Default implementation of the BundleClassLoaderHelper. Uses introspection to
* support equinox-3.5 and felix-2.0.0
*/
public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
{
private static boolean identifiedOsgiImpl = false;
private static Class BundleWiringClass = null;
private static Method BundleWiringClass_getClassLoader_method = null;
private static Method BundleClass_adapt_method = null;
private static boolean isEquinox = false;
private static boolean isFelix = false;
private static void init(Bundle bundle)
{
identifiedOsgiImpl = true;
try
{
BundleWiringClass = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring");
if (BundleWiringClass != null)
{
BundleWiringClass_getClassLoader_method = BundleWiringClass.getDeclaredMethod("getClassLoader", new Class[] {});
BundleClass_adapt_method = bundle.getClass().getDeclaredMethod("adapt", new Class[] { Class.class });
BundleClass_adapt_method.setAccessible(true);
return;
}
}
catch (Throwable t)
{
//nevermind: an older version of OSGi where BundleWiring is not availble
//t.printStackTrace();
}
if (!bundle.getClass().getName().startsWith("org.apache.felix"))
{
try
{
isEquinox = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost") != null;
}
catch (Throwable t)
{
isEquinox = false;
}
}
if (!isEquinox)
{
try
{
isFelix = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl") != null;
}
catch (Throwable t2)
{
isFelix = false;
}
}
}
/**
* Assuming the bundle is started.
*
* @param bundle
* @return classloader object
*/
public ClassLoader getBundleClassLoader(Bundle bundle)
{
//Older OSGi implementations:
String bundleActivator = (String) bundle.getHeaders().get("Bundle-Activator");
if (bundleActivator == null)
{
bundleActivator = (String) bundle.getHeaders().get("Jetty-ClassInBundle");
}
if (bundleActivator != null)
{
try
{
return bundle.loadClass(bundleActivator).getClassLoader();
}
catch (ClassNotFoundException e)
{
// should not happen as we are called if the bundle is started
// anyways.
e.printStackTrace();
}
}
// resort to introspection
if (!identifiedOsgiImpl)
{
init(bundle);
}
//This works for OSGi 4.2 and more recent. Aka version 1.6
//It is using ava reflection to execute:
//(BundleClassLoader) bundle.adapt(BundleWiring.class).getClassLoader()
if (BundleClass_adapt_method != null && BundleWiringClass_getClassLoader_method != null)
{
try
{
Object bundleWiring = BundleClass_adapt_method.invoke(bundle, BundleWiringClass);
return (ClassLoader)BundleWiringClass_getClassLoader_method.invoke(bundleWiring, new Object[] {});
}
catch (Throwable t)
{
t.printStackTrace();
return null;
}
}
if (isEquinox)
{
return internalGetEquinoxBundleClassLoader(bundle);
}
else if (isFelix) { return internalGetFelixBundleClassLoader(bundle); }
return null;
}
private static Method Equinox_BundleHost_getBundleLoader_method;
private static Method Equinox_BundleLoader_createClassLoader_method;
private static ClassLoader internalGetEquinoxBundleClassLoader(Bundle bundle)
{
// assume equinox:
try
{
if (Equinox_BundleHost_getBundleLoader_method == null)
{
Equinox_BundleHost_getBundleLoader_method =
bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost").getDeclaredMethod("getBundleLoader", new Class[] {});
Equinox_BundleHost_getBundleLoader_method.setAccessible(true);
}
Object bundleLoader = Equinox_BundleHost_getBundleLoader_method.invoke(bundle, new Object[] {});
if (Equinox_BundleLoader_createClassLoader_method == null && bundleLoader != null)
{
Equinox_BundleLoader_createClassLoader_method =
bundleLoader.getClass().getClassLoader().loadClass("org.eclipse.osgi.internal.loader.BundleLoader").getDeclaredMethod("createClassLoader", new Class[] {});
Equinox_BundleLoader_createClassLoader_method.setAccessible(true);
}
return (ClassLoader) Equinox_BundleLoader_createClassLoader_method.invoke(bundleLoader, new Object[] {});
}
catch (Throwable t)
{
t.printStackTrace();
}
return null;
}
private static Field Felix_BundleImpl_m_modules_field;
private static Field Felix_ModuleImpl_m_classLoader_field;
private static Field Felix_BundleImpl_m_revisions_field;
private static ClassLoader internalGetFelixBundleClassLoader(Bundle bundle)
{
// assume felix:
try
{
// now get the current module from the bundle.
// and return the private field m_classLoader of ModuleImpl
if (Felix_BundleImpl_m_modules_field == null)
{
Felix_BundleImpl_m_modules_field = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl").getDeclaredField("m_modules");
Felix_BundleImpl_m_modules_field.setAccessible(true);
}
// Figure out which version of the modules is exported
Object currentModuleImpl;
try
{
Object[] moduleArray = (Object[]) Felix_BundleImpl_m_modules_field.get(bundle);
currentModuleImpl = moduleArray[moduleArray.length - 1];
}
catch (Throwable t2)
{
@SuppressWarnings("unchecked")
List<Object> moduleArray = (List<Object>) Felix_BundleImpl_m_modules_field.get(bundle);
currentModuleImpl = moduleArray.get(moduleArray.size() - 1);
}
if (Felix_ModuleImpl_m_classLoader_field == null && currentModuleImpl != null)
{
Felix_ModuleImpl_m_classLoader_field = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.ModuleImpl").getDeclaredField("m_classLoader");
Felix_ModuleImpl_m_classLoader_field.setAccessible(true);
}
// first make sure that the classloader is ready:
// the m_classLoader field must be initialized by the
// ModuleImpl.getClassLoader() private method.
ClassLoader cl = (ClassLoader) Felix_ModuleImpl_m_classLoader_field.get(currentModuleImpl);
if (cl == null)
{
// looks like it was not ready:
// the m_classLoader field must be initialized by the
// ModuleImpl.getClassLoader() private method.
// this call will do that.
bundle.loadClass("java.lang.Object");
cl = (ClassLoader) Felix_ModuleImpl_m_classLoader_field.get(currentModuleImpl);
return cl;
}
else
{
return cl;
}
}
catch (Throwable t)
{
t.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,446 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.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;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.FileResource;
import org.eclipse.jetty.util.resource.Resource;
import org.osgi.framework.Bundle;
/**
* From a bundle to its location on the filesystem.
* Often assumes the bundle is not a jar.
*
* @author hmalphettes
*/
public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
{
// hack to locate the file-system directly from the bundle.
// support equinox, felix and nuxeo's osgi implementations.
// not tested on nuxeo and felix just yet.
// The url nuxeo and felix return is created directly from the File so it
// should work.
private static Field BUNDLE_ENTRY_FIELD = null;
private static Field FILE_FIELD = null;
private static Field BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY = null;// ZipBundleFile
// inside
// DirZipBundleEntry
private static Field ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = null;// ZipFile
/**
* Works with equinox, felix, nuxeo and probably more. Not exactly in the
* spirit of OSGi but quite necessary to support self-contained webapps and
* other situations.
*
* @param bundle The bundle
* @return Its installation location as a file.
* @throws Exception
*/
public File getBundleInstallLocation(Bundle bundle) throws Exception
{
// String installedBundles = System.getProperty("osgi.bundles");
// grab the MANIFEST.MF's url
// and then do what it takes.
URL url = bundle.getEntry("/META-INF/MANIFEST.MF");
if (url.getProtocol().equals("file"))
{
// some osgi frameworks do use the file protocole directly in some
// situations. Do use the FileResource to transform the URL into a
// File: URL#toURI is broken
return new FileResource(url).getFile().getParentFile().getParentFile();
}
else if (url.getProtocol().equals("bundleentry"))
{
// say hello to equinox who has its own protocol.
// we use introspection like there is no tomorrow to get access to
// the File
URLConnection con = url.openConnection();
con.setUseCaches(Resource.getDefaultUseCaches()); // work around
// problems where
// url connections
// cache
// references to
// jars
if (BUNDLE_ENTRY_FIELD == null)
{
BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry");
BUNDLE_ENTRY_FIELD.setAccessible(true);
}
Object bundleEntry = BUNDLE_ENTRY_FIELD.get(con);
if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.FileBundleEntry"))
{
if (FILE_FIELD == null)
{
FILE_FIELD = bundleEntry.getClass().getDeclaredField("file");
FILE_FIELD.setAccessible(true);
}
File f = (File) FILE_FIELD.get(bundleEntry);
return f.getParentFile().getParentFile();
}
else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleEntry"))
{
url = bundle.getEntry("/");
con = url.openConnection();
con.setDefaultUseCaches(Resource.getDefaultUseCaches());
if (BUNDLE_ENTRY_FIELD == null)
{// this one will be a DirZipBundleEntry
BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry");
BUNDLE_ENTRY_FIELD.setAccessible(true);
}
bundleEntry = BUNDLE_ENTRY_FIELD.get(con);
if (BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY == null)
{
BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY = bundleEntry.getClass().getDeclaredField("bundleFile");
BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY.setAccessible(true);
}
Object zipBundleFile = BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY.get(bundleEntry);
if (ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE == null)
{
ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = zipBundleFile.getClass().getDeclaredField("zipFile");
ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.setAccessible(true);
}
ZipFile zipFile = (ZipFile) ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.get(zipBundleFile);
return new File(zipFile.getName());
}
else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.DirZipBundleEntry"))
{
// that will not happen as we did ask for the manifest not a
// directory.
}
}
else if ("bundle".equals(url.getProtocol()))
{
// observed this on felix-2.0.0
String location = bundle.getLocation();
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;
}
else
{
//Resort to introspection on felix:
return getBundleInstallLocationInFelix(bundle);
}
}
return null;
}
/**
* Locate a file inside a bundle.
*
* @param bundle
* @param path
* @return file object
* @throws Exception
*/
public File getFileInBundle(Bundle bundle, String path) throws Exception
{
if (path != null && path.length() > 0 && path.charAt(0) == '/')
{
path = path.substring(1);
}
File bundleInstall = getBundleInstallLocation(bundle);
File webapp = path != null && path.length() != 0 ? new File(bundleInstall, path) : bundleInstall;
if (!webapp.exists()) { throw new IllegalArgumentException("Unable to locate " + path
+ " inside "
+ bundle.getSymbolicName()
+ " ("
+ (bundleInstall != null ? bundleInstall.getAbsolutePath() : " no_bundle_location ")
+ ")"); }
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
* inside it and search for jars that it returns.
* <p>
* Good enough for our purpose (TldLocationsCache when it scans for tld
* files inside jars alone. In fact we only support the second situation for
* development purpose where the bundle was imported in pde and the classes
* kept in a jar.
* </p>
*
* @param bundle
* @return The jar(s) file that is either the bundle itself, either the jars
* embedded inside it.
*/
public File[] locateJarsInsideBundle(Bundle bundle) throws Exception
{
File jasperLocation = getBundleInstallLocation(bundle);
if (jasperLocation.isDirectory())
{
// try to find the jar files inside this folder
ArrayList<File> urls = new ArrayList<File>();
for (File f : jasperLocation.listFiles())
{
if (f.getName().endsWith(".jar") && f.isFile())
{
urls.add(f);
}
else if (f.isDirectory() && f.getName().equals("lib"))
{
for (File f2 : jasperLocation.listFiles())
{
if (f2.getName().endsWith(".jar") && f2.isFile())
{
urls.add(f2);
}
}
}
}
return urls.toArray(new File[urls.size()]);
}
else
{
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();
conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
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)
{
System.err.println("Unable to locate the OSGi url: '" + url + "'.");
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();
conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
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;
}
// Felix introspection
private static Method Felix_BundleImpl_getArchive_method;
private static Method Felix_BundleArchive_getCurrentRevision_method;
private static Method Felix_BundleRevision_getRevisionRootDir_method;
private static boolean felixIntroSpectionDone = false;
/**
* Introspection of the implementation classes of Felix-3.x and Felix-4.x.
* <p>
* See org.apache.felix.framework.cache
* In pseudo code:
* <code>
* File revRootDir = BundleImpl.getArchive().getCurrentRevision().getRevisionRootDir();
* return new File(revRootDir, bundle.jar) if it exists?
* else return revRootDir
* </p>
* @param bundle
* @return The File or null if we failed to find it.
*/
private static File getBundleInstallLocationInFelix(Bundle bundle)
{
if (Felix_BundleImpl_getArchive_method == null) {
if (felixIntroSpectionDone)
{
return null;
}
felixIntroSpectionDone = true;
try
{
Felix_BundleImpl_getArchive_method = bundle.getClass().getDeclaredMethod("getArchive", new Class[] {});
Felix_BundleImpl_getArchive_method.setAccessible(true);
Object archive = Felix_BundleImpl_getArchive_method.invoke(bundle);
Class bundleArchiveClass = archive.getClass();
Felix_BundleArchive_getCurrentRevision_method = bundleArchiveClass.getDeclaredMethod("getCurrentRevision", new Class[] {});
Felix_BundleArchive_getCurrentRevision_method.setAccessible(true);
Object revision = Felix_BundleArchive_getCurrentRevision_method.invoke(archive);
Class bundleRevisionClass = revision.getClass();
Felix_BundleRevision_getRevisionRootDir_method = bundleRevisionClass.getMethod("getRevisionRootDir", new Class[] {});
Felix_BundleRevision_getRevisionRootDir_method.setAccessible(true);
}
catch (Throwable t)
{
//nevermind?
//t.printStackTrace();
Felix_BundleImpl_getArchive_method = null;
return null;
}
}
if (Felix_BundleImpl_getArchive_method != null)
{
try
{
Object archive = Felix_BundleImpl_getArchive_method.invoke(bundle);
Object revision = Felix_BundleArchive_getCurrentRevision_method.invoke(archive);
File revRootDir = (File)Felix_BundleRevision_getRevisionRootDir_method.invoke(revision);
//System.err.println("Got the archive revision root dir " + revRootDir.getAbsolutePath());
File bundleJar = new File(revRootDir, "bundle.jar");
if (bundleJar.exists())
{
//bundle.jar is hardcoded in org.apache.felix.framework.cache.JarRevision
//when it is not a bundle.jar, then the bundle location starts with 'file:' and we have already
//taken care if that scheme earlier.
return bundleJar;
}
else //sanity check?: if (new File(revRootDir, "META-INF/MANIFEST.MF").exists())
{
//this is a DirectoryRevision
return revRootDir;
}
}
catch (Throwable t)
{
//best effort: nevermind
//t.printStackTrace();
}
}
return null;
}
// -- end Felix introspection
}

View File

@ -0,0 +1,383 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.utils.internal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
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
* attached to this bundle and "activate" them.
*/
public class PackageAdminServiceTracker implements ServiceListener
{
private BundleContext _context;
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)
{
INSTANCE = this;
_context = context;
if (!setup())
{
try
{
_context.addServiceListener(this, "(objectclass=" + PackageAdmin.class.getName() + ")");
}
catch (InvalidSyntaxException e)
{
e.printStackTrace(); // won't happen
}
}
}
/**
* @return true if the fragments were activated by this method.
*/
private boolean setup()
{
ServiceReference sr = _context.getServiceReference(PackageAdmin.class.getName());
_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;
}
/**
* Invokes the optional BundleActivator in each fragment. By convention the
* bundle activator for a fragment must be in the package that is defined by
* the symbolic name of the fragment and the name of the class must be
* 'FragmentActivator'.
*
* @param event The <code>ServiceEvent</code> object.
*/
public void serviceChanged(ServiceEvent event)
{
if (event.getType() == ServiceEvent.REGISTERED)
{
invokeFragmentActivators(event.getServiceReference());
}
}
/**
* Helper to access the PackageAdmin and return the fragments hosted by a
* bundle. when we drop the support for the older versions of OSGi, we will
* stop using the PackageAdmin service.
*
* @param bundle
* @return
*/
public Bundle[] getFragments(Bundle bundle)
{
ServiceReference sr = _context.getServiceReference(PackageAdmin.class.getName());
if (sr == null)
{// we should never be here really.
return null;
}
PackageAdmin admin = (PackageAdmin) _context.getService(sr);
return admin.getFragments(bundle);
}
/**
* Returns the fragments and the required-bundles of a bundle. Recursively
* collect the required-bundles and fragment when the directive
* visibility:=reexport is added to a required-bundle.
*
* @param bundle
* @param webFragOrAnnotationOrResources
* @return
*/
public Bundle[] getFragmentsAndRequiredBundles(Bundle bundle)
{
ServiceReference sr = _context.getServiceReference(PackageAdmin.class.getName());
if (sr == null)
{// we should never be here really.
return null;
}
PackageAdmin admin = (PackageAdmin) _context.getService(sr);
LinkedHashMap<String, Bundle> deps = new LinkedHashMap<String, Bundle>();
collectFragmentsAndRequiredBundles(bundle, admin, deps, false);
return deps.values().toArray(new Bundle[deps.size()]);
}
/**
* Returns the fragments and the required-bundles. Collects them
* transitively when the directive 'visibility:=reexport' is added to a
* required-bundle.
*
* @param bundle
* @param webFragOrAnnotationOrResources
* @return
*/
protected void collectFragmentsAndRequiredBundles(Bundle bundle, PackageAdmin admin, Map<String, Bundle> deps, boolean onlyReexport)
{
Bundle[] fragments = admin.getFragments(bundle);
if (fragments != null)
{
// Also add the bundles required by the fragments.
// this way we can inject onto an existing web-bundle a set of
// bundles that extend it
for (Bundle f : fragments)
{
if (!deps.keySet().contains(f.getSymbolicName()))
{
deps.put(f.getSymbolicName(), f);
collectRequiredBundles(f, admin, deps, onlyReexport);
}
}
}
collectRequiredBundles(bundle, admin, deps, onlyReexport);
}
/**
* A simplistic but good enough parser for the Require-Bundle header. Parses
* the version range attribute and the visibility directive.
*
* @param onlyReexport true to collect resources and web-fragments
* transitively if and only if the directive visibility is
* reexport.
* @param bundle
* @return The map of required bundles associated to the value of the
* jetty-web attribute.
*/
protected void collectRequiredBundles(Bundle bundle, PackageAdmin admin, Map<String, Bundle> deps, boolean onlyReexport)
{
String requiredBundleHeader = (String) bundle.getHeaders().get("Require-Bundle");
if (requiredBundleHeader == null) { return; }
StringTokenizer tokenizer = new ManifestTokenizer(requiredBundleHeader);
while (tokenizer.hasMoreTokens())
{
String tok = tokenizer.nextToken().trim();
StringTokenizer tokenizer2 = new StringTokenizer(tok, ";");
String symbolicName = tokenizer2.nextToken().trim();
if (deps.keySet().contains(symbolicName))
{
// was already added. 2 dependencies pointing at the same
// bundle.
continue;
}
String versionRange = null;
boolean reexport = false;
while (tokenizer2.hasMoreTokens())
{
String next = tokenizer2.nextToken().trim();
if (next.startsWith("bundle-version="))
{
if (next.startsWith("bundle-version=\"") || next.startsWith("bundle-version='"))
{
versionRange = next.substring("bundle-version=\"".length(), next.length() - 1);
}
else
{
versionRange = next.substring("bundle-version=".length());
}
}
else if (next.equals("visibility:=reexport"))
{
reexport = true;
}
}
if (!reexport && onlyReexport) { return; }
Bundle[] reqBundles = admin.getBundles(symbolicName, versionRange);
if (reqBundles != null && reqBundles.length != 0)
{
Bundle reqBundle = null;
for (Bundle b : reqBundles)
{
if (b.getState() == Bundle.ACTIVE || b.getState() == Bundle.STARTING)
{
reqBundle = b;
break;
}
}
if (reqBundle == null)
{
// strange? in OSGi with Require-Bundle,
// the dependent bundle is supposed to be active already
reqBundle = reqBundles[0];
}
deps.put(reqBundle.getSymbolicName(), reqBundle);
collectFragmentsAndRequiredBundles(reqBundle, admin, deps, true);
}
}
}
private void invokeFragmentActivators(ServiceReference sr)
{
PackageAdmin admin = (PackageAdmin) _context.getService(sr);
Bundle[] fragments = admin.getFragments(_context.getBundle());
if (fragments == null) { return; }
for (Bundle frag : fragments)
{
// find a convention to look for a class inside the fragment.
try
{
String fragmentActivator = frag.getSymbolicName() + ".FragmentActivator";
Class<?> c = Class.forName(fragmentActivator);
if (c != null)
{
BundleActivator bActivator = (BundleActivator) c.newInstance();
bActivator.start(_context);
_activatedFragments.add(bActivator);
}
}
catch (NullPointerException e)
{
// e.printStackTrace();
}
catch (InstantiationException e)
{
// e.printStackTrace();
}
catch (IllegalAccessException e)
{
// e.printStackTrace();
}
catch (ClassNotFoundException e)
{
// e.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
public void stop()
{
INSTANCE = null;
for (BundleActivator fragAct : _activatedFragments)
{
try
{
fragAct.stop(_context);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
/**
* @return true if the framework has completed all the start levels.
*/
public boolean frameworkHasCompletedAutostarts()
{
return _startLevel == null ? true : _startLevel.getStartLevel() >= _maxStartLevel;
}
private static class ManifestTokenizer extends StringTokenizer
{
public ManifestTokenizer(String header)
{
super(header, ",");
}
@Override
public String nextToken()
{
String token = super.nextToken();
while (hasOpenQuote(token) && hasMoreTokens())
{
token += "," + super.nextToken();
}
return token;
}
private boolean hasOpenQuote(String token)
{
int i = -1;
do
{
int quote = getQuote(token, i + 1);
if (quote < 0) { return false; }
i = token.indexOf(quote, i + 1);
i = token.indexOf(quote, i + 1);
}
while (i >= 0);
return true;
}
private int getQuote(String token, int offset)
{
int i = token.indexOf('"', offset);
int j = token.indexOf('\'', offset);
if (i < 0)
{
if (j < 0)
{
return -1;
}
else
{
return '\'';
}
}
if (j < 0) { return '"'; }
if (i < j) { return '"'; }
return '\'';
}
}
}

View File

@ -18,12 +18,16 @@
package org.eclipse.jetty.osgi.annotations;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.annotations.AbstractDiscoverableAnnotationHandler;
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
import org.eclipse.jetty.annotations.ClassNameResolver;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;

View File

@ -0,0 +1,360 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import java.io.File;
import java.net.URL;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
import org.eclipse.jetty.osgi.boot.utils.OSGiClassLoader;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.JarResource;
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.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
/**
* AbstractContextProvider
*
*
*/
public abstract class AbstractContextProvider extends AbstractLifeCycle implements AppProvider
{
private static final Logger LOG = Log.getLogger(AbstractContextProvider.class);
private DeploymentManager _deploymentManager;
private ServerInstanceWrapper _serverWrapper;
/* ------------------------------------------------------------ */
/**
* BundleApp
*
*
*/
public class OSGiApp extends AbstractOSGiApp
{
private String _contextFile;
private ContextHandler _contextHandler;
private boolean _configured = false;
public OSGiApp(DeploymentManager manager, AppProvider provider, String originId, Bundle bundle, String contextFile)
{
super(manager, provider, bundle, originId);
_contextFile = contextFile;
}
public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String contextFile, String originId)
{
super(manager, provider, bundle, properties, originId);
_contextFile = contextFile;
}
public String getContextFile ()
{
return _contextFile;
}
public void setHandler(ContextHandler h)
{
_contextHandler = h;
}
public ContextHandler createContextHandler()
throws Exception
{
configureContextHandler();
return _contextHandler;
}
public void configureContextHandler()
throws Exception
{
if (_configured)
return;
_configured = true;
//Override for bundle root may have been set
String bundleOverrideLocation = (String)_properties.get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
if (bundleOverrideLocation == null)
bundleOverrideLocation = (String)_properties.get(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE);
//Location on filesystem of bundle or the bundle override location
File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(_bundle);
File root = (bundleOverrideLocation==null?bundleLocation:new File(bundleOverrideLocation));
Resource rootResource = Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(root.toURI().toURL()));
//try and make sure the rootResource is useable - if its a jar then make it a jar file url
if (rootResource.exists()&& !rootResource.isDirectory() && !rootResource.toString().startsWith("jar:"))
{
Resource jarResource = JarResource.newJarResource(rootResource);
if (jarResource.exists() && jarResource.isDirectory())
rootResource = jarResource;
}
//Set the base resource of the ContextHandler, if not already set, can also be overridden by the context xml file
if (_contextHandler != null && _contextHandler.getBaseResource() == null)
{
_contextHandler.setBaseResource(rootResource);
}
//Use a classloader that knows about the common jetty parent loader, and also the bundle
OSGiClassLoader classLoader = new OSGiClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps(), _bundle);
//if there is a context file, find it and apply it
if (_contextFile == null && _contextHandler == null)
throw new IllegalStateException("No context file or ContextHandler");
if (_contextFile != null)
{
//apply the contextFile, creating the ContextHandler, the DeploymentManager will register it in the ContextHandlerCollection
Resource res = null;
//try to find the context file in the filesystem
if (_contextFile.startsWith("/"))
res = getFileAsResource(_contextFile);
//try to find it relative to jetty home
if (res == null)
{
//See if the specific server we are related to has jetty.home set
String jettyHome = (String)getServerInstanceWrapper().getServer().getAttribute(OSGiServerConstants.JETTY_HOME);
if (jettyHome != null)
res = getFileAsResource(jettyHome, _contextFile);
//try to see if a SystemProperty for jetty.home is set
if (res == null)
{
jettyHome = System.getProperty(OSGiServerConstants.JETTY_HOME);
if (jettyHome != null)
{
if (jettyHome.startsWith("\"") || jettyHome.startsWith("'"))
jettyHome = jettyHome.substring(1);
if (jettyHome.endsWith("\"") || (jettyHome.endsWith("'")))
jettyHome = jettyHome.substring(0,jettyHome.length()-1);
res = getFileAsResource(jettyHome, _contextFile);
if (LOG.isDebugEnabled()) LOG.debug("jetty home context file:"+res);
}
}
}
//try to find it relative to an override location that has been specified
if (res == null)
{
if (bundleOverrideLocation != null)
{
res = getFileAsResource(Resource.newResource(bundleOverrideLocation).getFile(), _contextFile);
if (LOG.isDebugEnabled()) LOG.debug("Bundle override location context file:"+res);
}
}
//try to find it relative to the bundle in which it is being deployed
if (res == null)
{
if (_contextFile.startsWith("./"))
_contextFile = _contextFile.substring(1);
if (!_contextFile.startsWith("/"))
_contextFile = "/" + _contextFile;
URL contextURL = _bundle.getEntry(_contextFile);
if (contextURL != null)
res = Resource.newResource(contextURL);
}
//apply the context xml file, either to an existing ContextHandler, or letting the
//it create the ContextHandler as necessary
if (res != null)
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
LOG.debug("Context classloader = " + cl);
try
{
Thread.currentThread().setContextClassLoader(classLoader);
XmlConfiguration xmlConfiguration = new XmlConfiguration(res.getInputStream());
HashMap properties = new HashMap();
//put the server instance in
properties.put("Server", getServerInstanceWrapper().getServer());
//put in the location of the bundle root
properties.put("bundle.root", rootResource.toString());
// insert the bundle's location as a property.
xmlConfiguration.getProperties().putAll(properties);
if (_contextHandler == null)
_contextHandler = (ContextHandler) xmlConfiguration.configure();
else
xmlConfiguration.configure(_contextHandler);
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
}
//Set up the class loader we created
_contextHandler.setClassLoader(classLoader);
//If a bundle/service property specifies context path, let it override the context xml
String contextPath = (String)_properties.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (contextPath == null)
contextPath = (String)_properties.get(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH);
if (contextPath != null)
_contextHandler.setContextPath(contextPath);
//osgi Enterprise Spec r4 p.427
_contextHandler.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, _bundle.getBundleContext());
//make sure we protect also the osgi dirs specified by OSGi Enterprise spec
String[] targets = _contextHandler.getProtectedTargets();
int length = (targets==null?0:targets.length);
String[] updatedTargets = null;
if (targets != null)
{
updatedTargets = new String[length+OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(targets, 0, updatedTargets, 0, length);
}
else
updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length);
_contextHandler.setProtectedTargets(updatedTargets);
}
private Resource getFileAsResource (String dir, String file)
{
Resource r = null;
try
{
File asFile = new File (dir, file);
if (asFile.exists())
r = Resource.newResource(asFile);
}
catch (Exception e)
{
r = null;
}
return r;
}
private Resource getFileAsResource (String file)
{
Resource r = null;
try
{
File asFile = new File (file);
if (asFile.exists())
r = Resource.newResource(asFile);
}
catch (Exception e)
{
r = null;
}
return r;
}
private Resource getFileAsResource (File dir, String file)
{
Resource r = null;
try
{
File asFile = new File (dir, file);
if (asFile.exists())
r = Resource.newResource(asFile);
}
catch (Exception e)
{
r = null;
}
return r;
}
}
/* ------------------------------------------------------------ */
public AbstractContextProvider(ServerInstanceWrapper wrapper)
{
_serverWrapper = wrapper;
}
/* ------------------------------------------------------------ */
public ServerInstanceWrapper getServerInstanceWrapper()
{
return _serverWrapper;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.deploy.AppProvider#createContextHandler(org.eclipse.jetty.deploy.App)
*/
public ContextHandler createContextHandler(App app) throws Exception
{
if (app == null)
return null;
if (!(app instanceof OSGiApp))
throw new IllegalStateException(app+" is not a BundleApp");
//Create a ContextHandler suitable to deploy in OSGi
ContextHandler h = ((OSGiApp)app).createContextHandler();
return h;
}
/* ------------------------------------------------------------ */
public void setDeploymentManager(DeploymentManager deploymentManager)
{
_deploymentManager = deploymentManager;
}
/* ------------------------------------------------------------ */
public DeploymentManager getDeploymentManager()
{
return _deploymentManager;
}
}

View File

@ -0,0 +1,120 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import java.util.Dictionary;
import java.util.Hashtable;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
/**
* AbstractBundleApp
*
*
*/
public abstract class AbstractOSGiApp extends App
{
protected Bundle _bundle;
protected Dictionary _properties;
protected ServiceRegistration _registration;
/* ------------------------------------------------------------ */
public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
{
super(manager, provider, originId);
_properties = bundle.getHeaders();
_bundle = bundle;
}
/* ------------------------------------------------------------ */
public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId)
{
super(manager, provider, originId);
_properties = properties;
_bundle = bundle;
}
/* ------------------------------------------------------------ */
public String getBundleSymbolicName()
{
return _bundle.getSymbolicName();
}
/* ------------------------------------------------------------ */
public String getBundleVersionAsString()
{
if (_bundle.getVersion() == null)
return null;
return _bundle.getVersion().toString();
}
/* ------------------------------------------------------------ */
public Bundle getBundle()
{
return _bundle;
}
/* ------------------------------------------------------------ */
public void setRegistration (ServiceRegistration registration)
{
_registration = registration;
}
/* ------------------------------------------------------------ */
public ServiceRegistration getRegistration ()
{
return _registration;
}
/* ------------------------------------------------------------ */
public void registerAsOSGiService() throws Exception
{
if (_registration == null)
{
Dictionary<String,String> properties = new Hashtable<String,String>();
properties.put(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK);
if (getBundleSymbolicName() != null)
properties.put(OSGiWebappConstants.OSGI_WEB_SYMBOLICNAME, getBundleSymbolicName());
if (getBundleVersionAsString() != null)
properties.put(OSGiWebappConstants.OSGI_WEB_VERSION, getBundleVersionAsString());
properties.put(OSGiWebappConstants.OSGI_WEB_CONTEXTPATH, getContextPath());
ServiceRegistration rego = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ContextHandler.class.getName(), getContextHandler(), properties);
setRegistration(rego);
}
}
/* ------------------------------------------------------------ */
protected void deregisterAsOSGiService() throws Exception
{
if (_registration == null)
return;
_registration.unregister();
_registration = null;
}
}

View File

@ -0,0 +1,552 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.internal.webapp.OSGiWebappClassLoader;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.packageadmin.PackageAdmin;
/**
* AbstractWebAppProvider
*
*
*/
public abstract class AbstractWebAppProvider extends AbstractLifeCycle implements AppProvider
{
private static final Logger LOG = Log.getLogger(AbstractWebAppProvider.class);
public static String __defaultConfigurations[] = {
"org.eclipse.jetty.osgi.boot.OSGiWebInfConfiguration",
"org.eclipse.jetty.webapp.WebXmlConfiguration",
"org.eclipse.jetty.osgi.boot.OSGiMetaInfConfiguration",
"org.eclipse.jetty.webapp.FragmentConfiguration",
"org.eclipse.jetty.webapp.JettyWebXmlConfiguration"//,
//"org.eclipse.jetty.osgi.boot.jsp.TagLibOSGiConfiguration"
};
public static void setDefaultConfigurations (String[] defaultConfigs)
{
__defaultConfigurations = defaultConfigs;
}
public static String[] getDefaultConfigurations ()
{
return __defaultConfigurations;
}
private boolean _parentLoaderPriority;
private String _defaultsDescriptor;
private boolean _extractWars = true; //See WebAppContext.extractWars
private String _tldBundles;
private DeploymentManager _deploymentManager;
private String[] _configurationClasses;
private ServerInstanceWrapper _serverWrapper;
/* ------------------------------------------------------------ */
/**
* OSGiApp
*
*
*/
public class OSGiApp extends AbstractOSGiApp
{
private String _contextPath;
private String _webAppPath;
private WebAppContext _webApp;
public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
{
super(manager, provider, bundle, originId);
}
public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId)
{
super(manager, provider, bundle, properties, originId);
}
public void setWebAppContext (WebAppContext webApp)
{
_webApp = webApp;
}
public String getContextPath()
{
return _contextPath;
}
public void setContextPath(String contextPath)
{
this._contextPath = contextPath;
}
public String getBundlePath()
{
return _webAppPath;
}
public void setWebAppPath(String path)
{
this._webAppPath = path;
}
public ContextHandler createContextHandler()
throws Exception
{
if (_webApp != null)
{
configureWebApp();
return _webApp;
}
createWebApp();
return _webApp;
}
protected void createWebApp ()
throws Exception
{
_webApp = newWebApp();
configureWebApp();
}
protected WebAppContext newWebApp ()
{
WebAppContext webApp = new WebAppContext();
webApp.setAttribute(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK);
//make sure we protect also the osgi dirs specified by OSGi Enterprise spec
String[] targets = webApp.getProtectedTargets();
String[] updatedTargets = null;
if (targets != null)
{
updatedTargets = new String[targets.length+OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(targets, 0, updatedTargets, 0, targets.length);
}
else
updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length);
webApp.setProtectedTargets(updatedTargets);
return webApp;
}
public void configureWebApp()
throws Exception
{
//TODO turn this around and let any context.xml file get applied first, and have the properties override
_webApp.setContextPath(_contextPath);
//osgi Enterprise Spec r4 p.427
_webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, _bundle.getBundleContext());
String overrideBundleInstallLocation = (String)_properties.get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
File bundleInstallLocation =
(overrideBundleInstallLocation == null
? BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(_bundle)
: new File(overrideBundleInstallLocation));
URL url = null;
//if the path wasn't set or it was ., then it is the root of the bundle's installed location
if (_webAppPath == null || _webAppPath.length() == 0 || ".".equals(_webAppPath))
{
url = bundleInstallLocation.toURI().toURL();
}
else
{
//Get the location of the root of the webapp inside the installed bundle
if (_webAppPath.startsWith("/") || _webAppPath.startsWith("file:"))
{
url = new File(_webAppPath).toURI().toURL();
}
else if (bundleInstallLocation != null && bundleInstallLocation.isDirectory())
{
url = new File(bundleInstallLocation, _webAppPath).toURI().toURL();
}
else if (bundleInstallLocation != null)
{
Enumeration<URL> urls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(_bundle, _webAppPath);
if (urls != null && urls.hasMoreElements())
url = urls.nextElement();
}
}
if (url == null)
{
throw new IllegalArgumentException("Unable to locate " + _webAppPath
+ " in "
+ (bundleInstallLocation != null ? bundleInstallLocation.getAbsolutePath() : "unlocated bundle '" + _bundle.getSymbolicName()+ "'"));
}
// converts bundleentry: protocol if necessary
_webApp.setWar(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(url).toString());
// Set up what has been configured on the provider
_webApp.setParentLoaderPriority(isParentLoaderPriority());
_webApp.setExtractWAR(isExtract());
if (getConfigurationClasses() != null)
_webApp.setConfigurationClasses(getConfigurationClasses());
else
_webApp.setConfigurationClasses(__defaultConfigurations);
if (getDefaultsDescriptor() != null)
_webApp.setDefaultsDescriptor(getDefaultsDescriptor());
//Set up configuration from manifest headers
//extra classpath
String tmp = (String)_properties.get(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH);
if (tmp != null)
_webApp.setExtraClasspath(tmp);
//web.xml
tmp = (String)_properties.get(OSGiWebappConstants.JETTY_WEB_XML_PATH);
if (tmp != null && tmp.trim().length() != 0)
{
File webXml = getFile (tmp, bundleInstallLocation);
if (webXml != null && webXml.exists())
_webApp.setDescriptor(webXml.getAbsolutePath());
}
//webdefault.xml
tmp = (String)_properties.get(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH);
if (tmp != null)
{
File defaultWebXml = getFile (tmp, bundleInstallLocation);
if (defaultWebXml != null && defaultWebXml.exists())
_webApp.setDefaultsDescriptor(defaultWebXml.getAbsolutePath());
}
//Handle Require-TldBundle
//This is a comma separated list of names of bundles that contain tlds that this webapp uses.
//We add them to the webapp classloader.
String requireTldBundles = (String)_properties.get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
String pathsToTldBundles = getPathsToRequiredBundles(requireTldBundles);
// make sure we provide access to all the jetty bundles by going
// through this bundle.
OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(_serverWrapper.getParentClassLoaderForWebapps(), _webApp, _bundle);
if (pathsToTldBundles != null)
webAppLoader.addClassPath(pathsToTldBundles);
_webApp.setClassLoader(webAppLoader);
// apply any META-INF/context.xml file that is found to configure
// the webapp first
applyMetaInfContextXml();
// pass the value of the require tld bundle so that the TagLibOSGiConfiguration
// can pick it up.
_webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles);
//Set up some attributes
// rfc66
_webApp.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:
_webApp.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), _bundle.getBundleContext());
// also pass the bundle directly. sometimes a bundle does not have a
// bundlecontext.
// it is still useful to have access to the Bundle from the servlet
// context.
_webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, _bundle);
}
protected String getPathsToRequiredBundles (String requireTldBundles)
throws Exception
{
if (requireTldBundles == null) return null;
ServiceReference ref = _bundle.getBundleContext().getServiceReference(org.osgi.service.packageadmin.PackageAdmin.class.getName());
PackageAdmin packageAdmin = (ref == null) ? null : (PackageAdmin)_bundle.getBundleContext().getService(ref);
if (packageAdmin == null)
throw new IllegalStateException("Unable to get PackageAdmin reference to locate required Tld bundles");
StringBuilder paths = new StringBuilder();
String[] symbNames = requireTldBundles.split(", ");
for (String symbName : symbNames)
{
Bundle[] bs = packageAdmin.getBundles(symbName, null);
if (bs == null || bs.length == 0)
{
throw new IllegalArgumentException("Unable to locate the bundle '" + symbName
+ "' specified by "
+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE
+ " in manifest of "
+ (_bundle == null ? "unknown" : _bundle.getSymbolicName()));
}
File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bs[0]);
if (paths.length() > 0) paths.append(", ");
paths.append(f.toURI().toURL().toString());
LOG.debug("getPathsToRequiredBundles: bundle path=" + bs[0].getLocation() + " uri=" + f.toURI());
}
return paths.toString();
}
protected void applyMetaInfContextXml()
throws Exception
{
if (_bundle == null) return;
if (_webApp == null) return;
ClassLoader cl = Thread.currentThread().getContextClassLoader();
LOG.debug("Context classloader = " + cl);
try
{
Thread.currentThread().setContextClassLoader(_webApp.getClassLoader());
//TODO replace this with getting the InputStream so we don't cache in URL
// find if there is a META-INF/context.xml file
URL contextXmlUrl = _bundle.getEntry("/META-INF/jetty-webapp-context.xml");
if (contextXmlUrl == null) return;
// Apply it just as the standard jetty ContextProvider would do
LOG.info("Applying " + contextXmlUrl + " to " + _webApp);
XmlConfiguration xmlConfiguration = new XmlConfiguration(contextXmlUrl);
HashMap properties = new HashMap();
properties.put("Server", getDeploymentManager().getServer());
xmlConfiguration.getProperties().putAll(properties);
xmlConfiguration.configure(_webApp);
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
private File getFile (String file, File bundleInstall)
{
if (file == null)
return null;
if (file.startsWith("/") || file.startsWith("file:/"))
return new File(file);
else
return new File(bundleInstall, file);
}
}
/* ------------------------------------------------------------ */
public AbstractWebAppProvider (ServerInstanceWrapper wrapper)
{
_serverWrapper = wrapper;
}
/* ------------------------------------------------------------ */
/**
* Get the parentLoaderPriority.
*
* @return the parentLoaderPriority
*/
public boolean isParentLoaderPriority()
{
return _parentLoaderPriority;
}
/* ------------------------------------------------------------ */
/**
* Set the parentLoaderPriority.
*
* @param parentLoaderPriority the parentLoaderPriority to set
*/
public void setParentLoaderPriority(boolean parentLoaderPriority)
{
_parentLoaderPriority = parentLoaderPriority;
}
/* ------------------------------------------------------------ */
/**
* Get the defaultsDescriptor.
*
* @return the defaultsDescriptor
*/
public String getDefaultsDescriptor()
{
return _defaultsDescriptor;
}
/* ------------------------------------------------------------ */
/**
* Set the defaultsDescriptor.
*
* @param defaultsDescriptor the defaultsDescriptor to set
*/
public void setDefaultsDescriptor(String defaultsDescriptor)
{
_defaultsDescriptor = defaultsDescriptor;
}
/* ------------------------------------------------------------ */
public boolean isExtract()
{
return _extractWars;
}
/* ------------------------------------------------------------ */
public void setExtract(boolean extract)
{
_extractWars = extract;
}
/* ------------------------------------------------------------ */
/**
* @param tldBundles Comma separated list of bundles that contain tld jars
* that should be setup on the jetty instances created here.
*/
public void setTldBundles(String tldBundles)
{
_tldBundles = tldBundles;
}
/* ------------------------------------------------------------ */
/**
* @return The list of bundles that contain tld jars that should be setup on
* the jetty instances created here.
*/
public String getTldBundles()
{
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;
}
/* ------------------------------------------------------------ */
public void setServerInstanceWrapper(ServerInstanceWrapper wrapper)
{
_serverWrapper = wrapper;
}
public ServerInstanceWrapper getServerInstanceWrapper()
{
return _serverWrapper;
}
/* ------------------------------------------------------------ */
/**
* @return
*/
public DeploymentManager getDeploymentManager()
{
return _deploymentManager;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.deploy.AppProvider#setDeploymentManager(org.eclipse.jetty.deploy.DeploymentManager)
*/
public void setDeploymentManager(DeploymentManager deploymentManager)
{
_deploymentManager = deploymentManager;
}
/* ------------------------------------------------------------ */
public ContextHandler createContextHandler(App app) throws Exception
{
if (app == null)
return null;
if (!(app instanceof OSGiApp))
throw new IllegalStateException(app+" is not a BundleApp");
//Create a WebAppContext suitable to deploy in OSGi
ContextHandler ch = ((OSGiApp)app).createContextHandler();
return ch;
}
/* ------------------------------------------------------------ */
public static String getOriginId(Bundle contributor, String path)
{
return contributor.getSymbolicName() + "-" + contributor.getVersion().toString() + (path.startsWith("/") ? path : "/" + path);
}
}

View File

@ -0,0 +1,169 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
/**
* BundleContextProvider
*
* Handles deploying bundles that define a context xml file for configuring them.
*
*
*/
public class BundleContextProvider extends AbstractContextProvider implements BundleProvider
{
private static final Logger LOG = Log.getLogger(AbstractContextProvider.class);
private Map<String, App> _appMap = new HashMap<String, App>();
private Map<Bundle, List<App>> _bundleMap = new HashMap<Bundle, List<App>>();
private ServiceRegistration _serviceRegForBundles;
/* ------------------------------------------------------------ */
public BundleContextProvider(ServerInstanceWrapper wrapper)
{
super(wrapper);
}
/* ------------------------------------------------------------ */
@Override
protected void doStart() throws Exception
{
//register as an osgi service for deploying contexts defined in a bundle, advertising the name of the jetty Server instance we are related to
Dictionary<String,String> properties = new Hashtable<String,String>();
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
_serviceRegForBundles = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(BundleProvider.class.getName(), this, properties);
super.doStart();
}
/* ------------------------------------------------------------ */
@Override
protected void doStop() throws Exception
{
//unregister ourselves
if (_serviceRegForBundles != null)
{
try
{
_serviceRegForBundles.unregister();
}
catch (Exception e)
{
LOG.warn(e);
}
}
}
/* ------------------------------------------------------------ */
/**
* @param bundle
* @param contextFiles
* @return
*/
public boolean bundleAdded (Bundle bundle) throws Exception
{
if (bundle == null)
return false;
String contextFiles = (String)bundle.getHeaders().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
if (contextFiles == null)
contextFiles = (String)bundle.getHeaders().get(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
if (contextFiles == null)
return false;
boolean added = false;
//bundle defines JETTY_CONTEXT_FILE_PATH header,
//a comma separated list of context xml files that each define a ContextHandler
//TODO: (could be WebAppContexts)
String[] tmp = contextFiles.split(",;");
for (String contextFile : tmp)
{
String originId = bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + "-"+contextFile;
OSGiApp app = new OSGiApp(getDeploymentManager(), this, originId, bundle, contextFile);
_appMap.put(originId,app);
List<App> apps = _bundleMap.get(bundle);
if (apps == null)
{
apps = new ArrayList<App>();
_bundleMap.put(bundle, apps);
}
apps.add(app);
getDeploymentManager().addApp(app);
}
return added; //true if even 1 context from this bundle was added
}
/* ------------------------------------------------------------ */
/**
* Bundle has been removed. If it was a context we deployed, undeploy it.
* @param bundle
*
* @return true if this was a context we had deployed, false otherwise
*/
public boolean bundleRemoved (Bundle bundle) throws Exception
{
List<App> apps = _bundleMap.remove(bundle);
boolean removed = false;
if (apps != null)
{
for (App app:apps)
{
_appMap.remove(app.getOriginId());
getDeploymentManager().removeApp(app);
removed = true;
}
}
return removed; //true if even 1 context was removed associated with this bundle
}
}

View File

@ -0,0 +1,28 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import org.osgi.framework.Bundle;
public interface BundleProvider
{
public boolean bundleAdded (Bundle bundle) throws Exception;
public boolean bundleRemoved (Bundle bundle) throws Exception;
}

View File

@ -0,0 +1,238 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
/**
* BundleWebAppProvider
*
* A Jetty Provider that knows how to deploy a WebApp contained inside a Bundle.
*
*/
public class BundleWebAppProvider extends AbstractWebAppProvider implements BundleProvider
{
private static final Logger LOG = Log.getLogger(AbstractWebAppProvider.class);
/**
* Map of Bundle to App. Used when a Bundle contains a webapp.
*/
private Map<Bundle, App> _bundleMap = new HashMap<Bundle, App>();
private ServiceRegistration _serviceRegForBundles;
/* ------------------------------------------------------------ */
/**
* @param wrapper
*/
public BundleWebAppProvider (ServerInstanceWrapper wrapper)
{
super(wrapper);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
*/
protected void doStart() throws Exception
{
//register as an osgi service for deploying bundles, advertising the name of the jetty Server instance we are related to
Dictionary<String,String> properties = new Hashtable<String,String>();
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
_serviceRegForBundles = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(BundleProvider.class.getName(), this, properties);
super.doStart();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
*/
@Override
protected void doStop() throws Exception
{
//unregister ourselves
if (_serviceRegForBundles != null)
{
try
{
_serviceRegForBundles.unregister();
}
catch (Exception e)
{
LOG.warn(e);
}
}
super.doStop();
}
/* ------------------------------------------------------------ */
/**
* A bundle has been added that could be a webapp
* @param bundle
*/
public boolean bundleAdded (Bundle bundle) throws Exception
{
if (bundle == null)
return false;
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
String contextPath = null;
try
{
Dictionary headers = bundle.getHeaders();
//does the bundle have a OSGiWebappConstants.JETTY_WAR_FOLDER_PATH
if (headers.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH) != null)
{
String base = (String)headers.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
contextPath = getContextPath(bundle);
String originId = getOriginId(bundle, base);
//TODO : we don't know whether an app is actually deployed, as deploymentManager swallows all
//exceptions inside the impl of addApp. Need to send the Event and also register as a service
//only if the deployment succeeded
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
app.setWebAppPath(base);
app.setContextPath(contextPath);
_bundleMap.put(bundle, app);
getDeploymentManager().addApp(app);
return true;
}
//does the bundle have a WEB-INF/web.xml
if (bundle.getEntry("/WEB-INF/web.xml") != null)
{
String base = ".";
contextPath = getContextPath(bundle);
String originId = getOriginId(bundle, base);
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
app.setContextPath(contextPath);
app.setWebAppPath(base);
_bundleMap.put(bundle, app);
getDeploymentManager().addApp(app);
return true;
}
//does the bundle define a OSGiWebappConstants.RFC66_WEB_CONTEXTPATH
if (headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) != null)
{
//Could be a static webapp with no web.xml
String base = ".";
contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
String originId = getOriginId(bundle,base);
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
app.setContextPath(contextPath);
app.setWebAppPath(base);
_bundleMap.put(bundle, app);
getDeploymentManager().addApp(app);
return true;
}
return false;
}
catch (Exception e)
{
throw e;
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
/* ------------------------------------------------------------ */
/**
* Bundle has been removed. If it was a webapp we deployed, undeploy it.
* @param bundle
*
* @return true if this was a webapp we had deployed, false otherwise
*/
public boolean bundleRemoved (Bundle bundle) throws Exception
{
App app = _bundleMap.remove(bundle);
if (app != null)
{
getDeploymentManager().removeApp(app);
return true;
}
return false;
}
/* ------------------------------------------------------------ */
private static String getContextPath(Bundle bundle)
{
Dictionary<?, ?> headers = bundle.getHeaders();
String contextPath = (String) headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (contextPath == null)
{
// extract from the last token of the bundle's location:
// (really ?could consider processing the symbolic name as an alternative
// the location will often reflect the version.
// maybe this is relevant when the file is a war)
String location = bundle.getLocation();
String toks[] = location.replace('\\', '/').split("/");
contextPath = toks[toks.length - 1];
// remove .jar, .war etc:
int lastDot = contextPath.lastIndexOf('.');
if (lastDot != -1)
contextPath = contextPath.substring(0, lastDot);
}
if (!contextPath.startsWith("/"))
contextPath = "/" + contextPath;
return contextPath;
}
}

View File

@ -66,8 +66,6 @@ public class JettyBootstrapActivator implements BundleActivator
private ServiceRegistration _registeredServer;
private Server _server;
private JettyContextHandlerServiceTracker _jettyContextHandlerTracker;
private PackageAdminServiceTracker _packageAdminServiceTracker;
@ -95,20 +93,21 @@ public class JettyBootstrapActivator implements BundleActivator
// should activate.
_packageAdminServiceTracker = new PackageAdminServiceTracker(context);
// track Server instances that we should support as deployment targets
// track jetty Server instances that we should support as deployment targets
_jettyServerServiceTracker = new JettyServerServiceTracker();
context.addServiceListener(_jettyServerServiceTracker, "(objectclass=" + Server.class.getName() + ")");
// track ContextHandler class instances and deploy them to one of the known Servers
_jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(_jettyServerServiceTracker);
_jettyContextHandlerTracker = new JettyContextHandlerServiceTracker();
context.addServiceListener(_jettyContextHandlerTracker, "(objectclass=" + ContextHandler.class.getName() + ")");
// Create a default jetty instance right now.
DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context);
// track Bundles and deploy those that represent webapps to one of the known Servers
_webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer());
_webBundleTracker.open();
WebBundleTrackerCustomizer customizer = new WebBundleTrackerCustomizer();
_webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, customizer);
customizer.setAndOpenWebBundleTracker(_webBundleTracker);
}
/**
@ -129,7 +128,6 @@ public class JettyBootstrapActivator implements BundleActivator
}
if (_jettyContextHandlerTracker != null)
{
_jettyContextHandlerTracker.stop();
context.removeServiceListener(_jettyContextHandlerTracker);
_jettyContextHandlerTracker = null;
}
@ -163,10 +161,6 @@ public class JettyBootstrapActivator implements BundleActivator
}
finally
{
if (_server != null)
{
_server.stop();
}
INSTANCE = null;
}
}

View File

@ -0,0 +1,61 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.bindings.StandardDeployer;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
/**
* OSGiDeployer
*
*
*/
public class OSGiDeployer extends StandardDeployer
{
/* ------------------------------------------------------------ */
public void processBinding(Node node, App app) throws Exception
{
//TODO how to NOT send this event if its not a webapp:
//OSGi Enterprise Spec only wants an event sent if its a webapp bundle (ie not a ContextHandler)
if (!(app instanceof AbstractOSGiApp))
{
super.processBinding(node,app);
}
else
{
EventSender.getInstance().send(EventSender.DEPLOYING_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
try
{
super.processBinding(node,app);
((AbstractOSGiApp)app).registerAsOSGiService();
EventSender.getInstance().send(EventSender.DEPLOYED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
}
catch (Exception e)
{
EventSender.getInstance().send(EventSender.FAILED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
throw e;
}
}
}
}

View File

@ -0,0 +1,116 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
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.MetaInfConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
public class OSGiMetaInfConfiguration extends MetaInfConfiguration
{
private static final Logger LOG = Log.getLogger(OSGiMetaInfConfiguration.class);
/**
* Inspect bundle fragments associated with the bundle of the webapp for web-fragment, resources, tlds.
*
* @see org.eclipse.jetty.webapp.MetaInfConfiguration#preConfigure(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
public void preConfigure(final WebAppContext context) throws Exception
{
List<Resource> frags = (List<Resource>) context.getAttribute(METAINF_FRAGMENTS);
List<Resource> resfrags = (List<Resource>) context.getAttribute(METAINF_RESOURCES);
List<Resource> tldfrags = (List<Resource>) context.getAttribute(METAINF_TLDS);
Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles((Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE));
//TODO not convinced we need to do this, as we added any fragment jars to the MetaData.webInfJars in OSGiWebInfConfiguration,
//so surely the web-fragments and resources tlds etc can be discovered normally?
for (Bundle frag : fragments)
{
URL webFrag = frag.getEntry("/META-INF/web-fragment.xml");
Enumeration<URL> resEnum = frag.findEntries("/META-INF/resources", "*", true);
Enumeration<URL> tldEnum = frag.findEntries("/META-INF", "*.tld", false);
if (webFrag != null || (resEnum != null && resEnum.hasMoreElements()) || (tldEnum != null && tldEnum.hasMoreElements()))
{
try
{
if (webFrag != null)
{
if (frags == null)
{
frags = new ArrayList<Resource>();
context.setAttribute(METAINF_FRAGMENTS, frags);
}
frags.add(Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(frag).toURI()));
}
if (resEnum != null && resEnum.hasMoreElements())
{
URL resourcesEntry = frag.getEntry("/META-INF/resources/");
if (resourcesEntry == null)
{
// probably we found some fragments to a
// bundle.
// those are already contributed.
// so we skip this.
}
else
{
if (resfrags == null)
{
resfrags = new ArrayList<Resource>();
context.setAttribute(METAINF_RESOURCES, resfrags);
}
resfrags.add(Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(resourcesEntry)));
}
}
if (tldEnum != null && tldEnum.hasMoreElements())
{
if (tldfrags == null)
{
tldfrags = new ArrayList<Resource>();
context.setAttribute(METAINF_TLDS, tldfrags);
}
while (tldEnum.hasMoreElements())
{
URL tldUrl = tldEnum.nextElement();
tldfrags.add(Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(tldUrl)));
}
}
}
catch (Exception e)
{
LOG.warn("Unable to locate the bundle " + frag.getBundleId(), e);
}
}
}
super.preConfigure(context);
}
}

View File

@ -23,6 +23,38 @@ package org.eclipse.jetty.osgi.boot;
*/
public class OSGiServerConstants
{
/**
* Usual system property used as the hostname for a typical jetty
* configuration.
*/
public static final String 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 JETTY_HOME_BUNDLE = "jetty.home.bundle";
/**
* Usual system property used as the hostname for a typical jetty
* configuration.
*/
public static final String JETTY_HOST = "jetty.host";
/**
* Usual system property used as the port for http for a typical jetty
* configuration.
*/
public static final String JETTY_PORT = "jetty.port";
/**
* Usual system property used as the port for https for a typical jetty
* configuration.
*/
public static final String JETTY_PORT_SSL = "jetty.port.ssl";
//for managed jetty instances, name of the configuration parameters
/**
* PID of the jetty servers's ManagedFactory

View File

@ -0,0 +1,44 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.bindings.StandardUndeployer;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
/**
* OSGiUndeployer
*
*
*/
public class OSGiUndeployer extends StandardUndeployer
{
/* ------------------------------------------------------------ */
public void processBinding(Node node, App app) throws Exception
{
EventSender.getInstance().send(EventSender.UNDEPLOYING_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
super.processBinding(node,app);
EventSender.getInstance().send(EventSender.UNDEPLOYED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
((AbstractOSGiApp)app).deregisterAsOSGiService();
}
}

View File

@ -0,0 +1,273 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.regex.Pattern;
import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
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.util.resource.ResourceCollection;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
/**
* OSGiWebInfConfiguration
*
* Handle adding resources found in bundle fragments, and add them into the
*/
public class OSGiWebInfConfiguration extends WebInfConfiguration
{
private static final Logger LOG = Log.getLogger(WebInfConfiguration.class);
public static final String CONTAINER_BUNDLE_PATTERN = "org.eclipse.jetty.server.webapp.containerIncludeBundlePattern";
/**
* Check to see if there have been any bundle symbolic names added of bundles that should be
* regarded as being on the container classpath, and scanned for fragments, tlds etc etc.
* This can be defined in:
* <ol>
* <li>SystemProperty SYS_PROP_TLD_BUNDLES</li>
* <li>DeployerManager.setContextAttribute CONTAINER_BUNDLE_PATTERN</li>
* </ol>
*
* We also allow individual bundles to specify particular bundles that might include TLDs via the Require-Tlds
* MANIFEST.MF header. This is processed in the TagLibOSGiConfiguration class.
*
* @see org.eclipse.jetty.webapp.WebInfConfiguration#preConfigure(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
public void preConfigure(final WebAppContext context) throws Exception
{
super.preConfigure(context);
//Check to see if there have been any bundle symbolic names added of bundles that should be
//regarded as being on the container classpath, and scanned for fragments, tlds etc etc.
//This can be defined in:
// 1. SystemProperty SYS_PROP_TLD_BUNDLES
// 2. DeployerManager.setContextAttribute CONTAINER_BUNDLE_PATTERN
String tmp = (String)context.getAttribute(CONTAINER_BUNDLE_PATTERN);
Pattern pattern = (tmp==null?null:Pattern.compile(tmp));
List<String> names = new ArrayList<String>();
tmp = System.getProperty("org.eclipse.jetty.osgi.tldbundles");
if (tmp != null)
{
StringTokenizer tokenizer = new StringTokenizer(tmp, ", \n\r\t", false);
while (tokenizer.hasMoreTokens())
names.add(tokenizer.nextToken());
}
HashSet<Resource> matchingResources = new HashSet<Resource>();
if ( !names.isEmpty() || pattern != null)
{
Bundle[] bundles = FrameworkUtil.getBundle(OSGiWebInfConfiguration.class).getBundleContext().getBundles();
for (Bundle bundle : bundles)
{
if (pattern != null)
{
// if bundle symbolic name matches the pattern
if (pattern.matcher(bundle.getSymbolicName()).matches())
{
//get the file location of the jar and put it into the list of container jars that will be scanned for stuff (including tlds)
matchingResources.addAll(getBundleAsResource(bundle));
}
}
if (names != null)
{
//if there is an explicit bundle name, then check if it matches
if (names.contains(bundle.getSymbolicName()))
matchingResources.addAll(getBundleAsResource(bundle));
}
}
}
for (Resource r:matchingResources)
{
context.getMetaData().addContainerJar(r);
}
}
/**
* Consider the fragment bundles associated with the bundle of the webapp being deployed.
*
*
* @see org.eclipse.jetty.webapp.WebInfConfiguration#findJars(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
protected List<Resource> findJars (WebAppContext context)
throws Exception
{
List<Resource> mergedResources = new ArrayList<Resource>();
//get jars from WEB-INF/lib if there are any
List<Resource> webInfJars = super.findJars(context);
if (webInfJars != null)
mergedResources.addAll(webInfJars);
//add fragment jars as if in WEB-INF/lib of the associated webapp
Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles((Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE));
for (Bundle frag : fragments)
{
File fragFile = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(frag);
mergedResources.add(Resource.newResource(fragFile.toURI()));
}
return mergedResources;
}
/**
* Allow fragments to supply some resources that are added to the baseResource of the webapp.
*
* The resources can be either prepended or appended to the baseResource.
*
* @see org.eclipse.jetty.webapp.WebInfConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
public void configure(WebAppContext context) throws Exception
{
TreeMap<String, Resource> patchResourcesPath = new TreeMap<String, Resource>();
TreeMap<String, Resource> appendedResourcesPath = new TreeMap<String, Resource>();
Bundle bundle = (Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
if (bundle != null)
{
//TODO anything we need to do to improve PackageAdminServiceTracker?
Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(bundle);
if (fragments != null && fragments.length != 0)
{
// sorted extra resource base found in the fragments.
// the resources are either overriding the resourcebase found in the
// web-bundle
// or appended.
// amongst each resource we sort them according to the alphabetical
// order
// of the name of the internal folder and the symbolic name of the
// fragment.
// this is useful to make sure that the lookup path of those
// resource base defined by fragments is always the same.
// This natural order could be abused to define the order in which
// the base resources are
// looked up.
for (Bundle frag : fragments)
{
String fragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH);
String patchFragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH);
if (fragFolder != null)
{
URL fragUrl = frag.getEntry(fragFolder);
if (fragUrl == null) { throw new IllegalArgumentException("Unable to locate " + fragFolder
+ " inside "
+ " the fragment '"
+ frag.getSymbolicName()
+ "'"); }
fragUrl = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(fragUrl);
String key = fragFolder.startsWith("/") ? fragFolder.substring(1) : fragFolder;
appendedResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(fragUrl));
}
if (patchFragFolder != null)
{
URL patchFragUrl = frag.getEntry(patchFragFolder);
if (patchFragUrl == null)
{
throw new IllegalArgumentException("Unable to locate " + patchFragUrl
+ " inside fragment '"+frag.getSymbolicName()+ "'");
}
patchFragUrl = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(patchFragUrl);
String key = patchFragFolder.startsWith("/") ? patchFragFolder.substring(1) : patchFragFolder;
patchResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(patchFragUrl));
}
}
if (!appendedResourcesPath.isEmpty())
context.setAttribute(WebInfConfiguration.RESOURCE_URLS, new ArrayList<Resource>(appendedResourcesPath.values()));
}
}
super.configure(context);
// place the patch resources at the beginning of the contexts's resource base
if (!patchResourcesPath.isEmpty())
{
Resource[] resources = new Resource[1+patchResourcesPath.size()];
ResourceCollection mergedResources = new ResourceCollection (patchResourcesPath.values().toArray(new Resource[patchResourcesPath.size()]));
System.arraycopy(patchResourcesPath.values().toArray(new Resource[patchResourcesPath.size()]), 0, resources, 0, patchResourcesPath.size());
resources[resources.length-1] = context.getBaseResource();
context.setBaseResource(new ResourceCollection(resources));
}
}
/**
* Resolves the bundle. Usually that would be a single URL per bundle. But we do some more work if there are jars
* embedded in the bundle.
*/
private List<Resource> getBundleAsResource(Bundle bundle)
throws Exception
{
List<Resource> resources = new ArrayList<Resource>();
File file = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle);
if (file.isDirectory())
{
for (File f : file.listFiles())
{
if (f.getName().endsWith(".jar") && f.isFile())
{
resources.add(Resource.newResource(f));
}
else if (f.isDirectory() && f.getName().equals("lib"))
{
for (File f2 : file.listFiles())
{
if (f2.getName().endsWith(".jar") && f2.isFile())
{
resources.add(Resource.newResource(f));
}
}
}
}
resources.add(Resource.newResource(file)); //TODO really???
}
else
{
resources.add(Resource.newResource(file));
}
return resources;
}
}

View File

@ -23,6 +23,20 @@ package org.eclipse.jetty.osgi.boot;
*/
public class OSGiWebappConstants
{
/** service property osgi.web.symbolicname. See OSGi r4 */
public static final String OSGI_WEB_SYMBOLICNAME = "osgi.web.symbolicname";
/** service property osgi.web.symbolicname. See OSGi r4 */
public static final String OSGI_WEB_VERSION = "osgi.web.version";
/** service property osgi.web.contextpath. See OSGi r4 */
public static final String OSGI_WEB_CONTEXTPATH = "osgi.web.contextpath";
/** See OSGi r4 p.427 */
public static final String OSGI_BUNDLECONTEXT = "osgi-bundlecontext";
/** url scheme to deploy war file as bundled webapp */
public static final String RFC66_WAR_URL_SCHEME = "war";
@ -59,32 +73,60 @@ public class OSGiWebappConstants
* this will override static resources with the same name in the web-bundle. */
public static final String JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH = "Jetty-WarPatchFragmentFolderPath";
// OSGi ContextHandler service properties.
/** web app context path */
/**
* web app context path
* @deprecated see RFC66_WEB_CONTEXTPATH
*/
public static final String SERVICE_PROP_CONTEXT_PATH = "contextPath";
/** Path to the web application base folder */
/**
* Path to the web application base folder
* @deprecated see JETTY_WAR_FOLDER_PATH
*/
public static final String SERVICE_PROP_WAR = "war";
/** Extra classpath */
/**
* Extra classpath
* @deprecated see JETTY_EXTRA_CLASSPATH
*/
public static final String SERVICE_PROP_EXTRA_CLASSPATH = "extraClasspath";
/** jetty context file path */
public static final String JETTY_EXTRA_CLASSPATH = "Jetty-extraClasspath";
/**
* jetty context file path
* @deprecated see JETTY_CONTEXT_FILE_PATH
*/
public static final String SERVICE_PROP_CONTEXT_FILE_PATH = "contextFilePath";
/** web.xml file path */
/**
* web.xml file path
* @deprecated see JETTY_WEB_XML_PATH
*/
public static final String SERVICE_PROP_WEB_XML_PATH = "webXmlFilePath";
/** defaultweb.xml file path */
public static final String JETTY_WEB_XML_PATH = "Jetty-WebXmlFilePath";
/**
* defaultweb.xml file path
* @deprecated see JETTY_DEFAULT_WEB_XML_PATH
*/
public static final String SERVICE_PROP_DEFAULT_WEB_XML_PATH = "defaultWebXmlFilePath";
public static final String JETTY_DEFAULT_WEB_XML_PATH = "Jetty-defaultWebXmlFilePath";
/**
* path to the base folder that overrides the computed bundle installation
* location if not null useful to install webapps or jetty context files
* that are in fact not embedded in a bundle
* @deprecated see JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE
*/
public static final String SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE = "thisBundleInstall";
public static final String JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE = "Jetty-bundleInstall";
/**
* Comma separated list of bundles that contain tld file used by the webapp.
*/
@ -94,4 +136,14 @@ public class OSGiWebappConstants
* Both the name of the manifest header and the name of the service property.
*/
public static final String SERVICE_PROP_REQUIRE_TLD_BUNDLE = REQUIRE_TLD_BUNDLE;
public static final String WATERMARK = "o.e.j.o.b.watermark";
/**
* Set of extra dirs that must not be served by osgi webapps
*/
public static final String[] DEFAULT_PROTECTED_OSGI_TARGETS = {"/osgi-inf", "/osgi-opts"};
}

View File

@ -0,0 +1,186 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
/**
* ServiceContextProvider
*
*
*/
public class ServiceContextProvider extends AbstractContextProvider implements ServiceProvider
{
private static final Logger LOG = Log.getLogger(AbstractContextProvider.class);
private Map<ServiceReference, App> _serviceMap = new HashMap<ServiceReference, App>();
private ServiceRegistration _serviceRegForServices;
/**
* ServiceApp
*
*
*/
public class ServiceApp extends OSGiApp
{
public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String contextFile, String originId)
{
super(manager, provider, bundle, properties, contextFile, originId);
}
public ServiceApp(DeploymentManager manager, AppProvider provider, String originId, Bundle bundle, String contextFile)
{
super(manager, provider, originId, bundle, contextFile);
}
@Override
public void registerAsOSGiService() throws Exception
{
//not applicable for apps that are already services
}
@Override
protected void deregisterAsOSGiService() throws Exception
{
//not applicable for apps that are already services
}
}
/* ------------------------------------------------------------ */
public ServiceContextProvider(ServerInstanceWrapper wrapper)
{
super(wrapper);
}
/* ------------------------------------------------------------ */
public boolean serviceAdded (ServiceReference serviceRef, ContextHandler context)
{
if (context == null || serviceRef == null)
return false;
String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return false; //this service represents a contexthandler that has already been registered as a service by another of our deployers
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
try
{
//See if there is a context file to apply to this pre-made context
String contextFile = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
if (contextFile == null)
contextFile = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
String[] keys = serviceRef.getPropertyKeys();
Dictionary properties = new Hashtable<String, Object>();
if (keys != null)
{
for (String key:keys)
properties.put(key, serviceRef.getProperty(key));
}
Bundle bundle = serviceRef.getBundle();
String originId = bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + "-"+contextFile;
ServiceApp app = new ServiceApp(getDeploymentManager(), this, bundle, properties, contextFile, originId);
app.setHandler(context); //set the pre=made ContextHandler instance
_serviceMap.put(serviceRef, app);
getDeploymentManager().addApp(app);
return true;
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
/* ------------------------------------------------------------ */
public boolean serviceRemoved (ServiceReference serviceRef, ContextHandler context)
{
if (context == null || serviceRef == null)
return false;
String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return false; //this service represents a contexthandler that will be deregistered as a service by another of our deployers
App app = _serviceMap.remove(serviceRef);
if (app != null)
{
getDeploymentManager().removeApp(app);
return true;
}
return false;
}
/* ------------------------------------------------------------ */
@Override
protected void doStart() throws Exception
{
//register as an osgi service for deploying contexts defined in a bundle, advertising the name of the jetty Server instance we are related to
Dictionary<String,String> properties = new Hashtable<String,String>();
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
//register as an osgi service for deploying contexts, advertising the name of the jetty Server instance we are related to
_serviceRegForServices = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ServiceProvider.class.getName(), this, properties);
super.doStart();
}
/* ------------------------------------------------------------ */
@Override
protected void doStop() throws Exception
{
//unregister ourselves
if (_serviceRegForServices != null)
{
try
{
_serviceRegForServices.unregister();
}
catch (Exception e)
{
LOG.warn(e);
}
}
super.doStop();
}
}

View File

@ -0,0 +1,29 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.osgi.framework.ServiceReference;
public interface ServiceProvider
{
public boolean serviceAdded (ServiceReference ref, ContextHandler handler) throws Exception;
public boolean serviceRemoved (ServiceReference ref, ContextHandler handler) throws Exception;
}

View File

@ -0,0 +1,248 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
/**
* ServiceWebAppProvider
*
* Jetty Provider that knows how to deploy a WebApp that has been registered as an OSGi service.
*
*/
public class ServiceWebAppProvider extends AbstractWebAppProvider implements ServiceProvider
{
private static final Logger LOG = Log.getLogger(AbstractWebAppProvider.class);
/**
* Map of ServiceRef to App. Used when it is an osgi service that is a WebAppContext.
*/
private Map<ServiceReference, App> _serviceMap = new HashMap<ServiceReference, App>();
private ServiceRegistration _serviceRegForServices;
/**
* ServiceApp
*
*
*/
public class ServiceApp extends OSGiApp
{
public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId)
{
super(manager, provider, bundle, properties, originId);
}
public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
{
super(manager, provider, bundle, originId);
}
@Override
public void registerAsOSGiService() throws Exception
{
//not applicable for apps that are already services
}
@Override
protected void deregisterAsOSGiService() throws Exception
{
//not applicable for apps that are already services
}
}
/* ------------------------------------------------------------ */
/**
* @param wrapper
*/
public ServiceWebAppProvider (ServerInstanceWrapper wrapper)
{
super(wrapper);
}
/* ------------------------------------------------------------ */
/**
* A webapp that was deployed as an osgi service has been added,
* and we want to deploy it.
*
* @param context the webapp
*/
public boolean serviceAdded (ServiceReference serviceRef, ContextHandler context)
{
if (context == null || !(context instanceof WebAppContext))
return false;
String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return false; //this service represents a webapp that has already been registered as a service by another of our deployers
WebAppContext webApp = (WebAppContext)context;
Dictionary properties = new Hashtable<String,String>();
String contextPath = (String)serviceRef.getProperty(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (contextPath == null)
contextPath = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH);
if (contextPath == null)
return false; //No context path
String base = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
if (base == null)
base = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_WAR);
if (base == null)
return false; //No webapp base
String webdefaultXml = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH);
if (webdefaultXml == null)
webdefaultXml = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_DEFAULT_WEB_XML_PATH);
if (webdefaultXml != null)
properties.put(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH, webdefaultXml);
String webXml = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH);
if (webXml == null)
webXml = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_WEB_XML_PATH);
if (webXml != null)
properties.put(OSGiWebappConstants.JETTY_WEB_XML_PATH, webXml);
String extraClassPath = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH);
if (extraClassPath == null)
extraClassPath = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH);
if (extraClassPath != null)
properties.put(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH, extraClassPath);
String bundleInstallOverride = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
if (bundleInstallOverride == null)
bundleInstallOverride = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE);
if (bundleInstallOverride != null)
properties.put(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE, bundleInstallOverride);
String requiredTlds = (String)serviceRef.getProperty(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
if (requiredTlds == null)
requiredTlds = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE);
if (requiredTlds != null)
properties.put(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requiredTlds);
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
try
{
String originId = getOriginId(serviceRef.getBundle(), base);
ServiceApp app = new ServiceApp(getDeploymentManager(), this, serviceRef.getBundle(), properties, originId);
app.setContextPath(contextPath);
app.setWebAppPath(base);
app.setWebAppContext(webApp); //set the pre=made webapp instance
_serviceMap.put(serviceRef, app);
getDeploymentManager().addApp(app);
return true;
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
/* ------------------------------------------------------------ */
/**
* @param context the webapp
*/
public boolean serviceRemoved (ServiceReference serviceRef, ContextHandler context)
{
if (context == null || !(context instanceof WebAppContext))
return false;
String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return false; //this service represents a contexthandler that will be deregistered as a service by another of our deployers
App app = _serviceMap.remove(serviceRef);
if (app != null)
{
getDeploymentManager().removeApp(app);
return true;
}
return false;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
*/
protected void doStart() throws Exception
{
//register as an osgi service for deploying bundles, advertising the name of the jetty Server instance we are related to
Dictionary<String,String> properties = new Hashtable<String,String>();
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
//register as an osgi service for deploying contexts (discovered as osgi services), advertising the name of the jetty Server instance we are related to
_serviceRegForServices = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ServiceProvider.class.getName(), this, properties);
super.doStart();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
*/
@Override
protected void doStop() throws Exception
{
//unregister ourselves
if (_serviceRegForServices != null)
{
try
{
_serviceRegForServices.unregister();
}
catch (Exception e)
{
LOG.warn(e);
}
}
super.doStop();
}
}

View File

@ -28,6 +28,7 @@ import java.util.StringTokenizer;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.log.Log;
@ -36,6 +37,9 @@ import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
/**
* DefaultJettyAtJettyHomeHelper
*
*
* 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.
@ -49,49 +53,20 @@ public class DefaultJettyAtJettyHomeHelper
* 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";
public static final String JETTY_ETC_FILES = OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS;
/**
* Set of config files to apply to a jetty Server instance if none are supplied by SYS_PROP_JETTY_ETC_FILES
*/
public static final String DEFAULT_JETTY_ETC_FILES = "jetty.xml,jetty-selector.xml,jetty-deployer.xml";
public static final String DEFAULT_JETTY_ETC_FILES = "etc/jetty.xml,etc/jetty-selector.xml,etc/jetty-deployer.xml";
/**
* Default location within bundle of a jetty home dir.
*/
public static final String DEFAULT_JETTYHOME = "/jettyhome";
/* ------------------------------------------------------------ */
/**
* Called by the JettyBootStrapActivator. If the system property jetty.home
* is defined and points to a folder, creates a corresponding jetty
@ -116,8 +91,8 @@ public class DefaultJettyAtJettyHomeHelper
*/
public static void startJettyAtJettyHome(BundleContext bundleContext) throws Exception
{
String jettyHomeSysProp = System.getProperty(SYS_PROP_JETTY_HOME);
String jettyHomeBundleSysProp = System.getProperty(SYS_PROP_JETTY_HOME_BUNDLE);
String jettyHomeSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME);
String jettyHomeBundleSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME_BUNDLE);
File jettyHome = null;
Bundle jettyHomeBundle = null;
if (jettyHomeSysProp != null)
@ -174,17 +149,18 @@ public class DefaultJettyAtJettyHomeHelper
// 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));
setProperty(properties, OSGiServerConstants.JETTY_HOME, System.getProperty(OSGiServerConstants.JETTY_HOME));
setProperty(properties, OSGiServerConstants.JETTY_HOST, System.getProperty(OSGiServerConstants.JETTY_HOST));
setProperty(properties, OSGiServerConstants.JETTY_PORT, System.getProperty(OSGiServerConstants.JETTY_PORT));
setProperty(properties, OSGiServerConstants.JETTY_PORT_SSL, System.getProperty(OSGiServerConstants.JETTY_PORT_SSL));
//register the Server instance as an OSGi service.
bundleContext.registerService(Server.class.getName(), server, properties);
// hookNestedConnectorToBridgeServlet(server);
}
/* ------------------------------------------------------------ */
/**
* Minimum setup for the location of the configuration files given a
* jettyhome folder. Reads the system property jetty.etc.config.urls and
@ -196,7 +172,7 @@ public class DefaultJettyAtJettyHomeHelper
*/
private static String getJettyConfigurationURLs(File jettyhome)
{
String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES, "etc/jetty.xml");
String jettyetc = System.getProperty(JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
StringTokenizer tokenizer = new StringTokenizer(jettyetc, ";,", false);
StringBuilder res = new StringBuilder();
while (tokenizer.hasMoreTokens())
@ -219,6 +195,8 @@ public class DefaultJettyAtJettyHomeHelper
return res.toString();
}
/* ------------------------------------------------------------ */
/**
* Minimum setup for the location of the configuration files given a
* configuration embedded inside a bundle. Reads the system property
@ -230,7 +208,7 @@ public class DefaultJettyAtJettyHomeHelper
*/
private static String getJettyConfigurationURLs(Bundle configurationBundle)
{
String files = System.getProperty(SYS_PROP_JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
String files = System.getProperty(JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
StringTokenizer tokenizer = new StringTokenizer(files, ";,", false);
StringBuilder res = new StringBuilder();
@ -246,7 +224,7 @@ public class DefaultJettyAtJettyHomeHelper
else
{
//relative file path
Enumeration<URL> enUrls = BundleFileLocatorHelper.DEFAULT.findEntries(configurationBundle, etcFile);
Enumeration<URL> enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, etcFile);
// default for org.eclipse.osgi.boot where we look inside
// jettyhome for the default embedded configuration.
@ -254,21 +232,22 @@ public class DefaultJettyAtJettyHomeHelper
// can define their own configuration.
if ((enUrls == null || !enUrls.hasMoreElements()))
{
String tmp = DEFAULT_JETTYHOME+"/etc/"+etcFile;
enUrls = BundleFileLocatorHelper.DEFAULT.findEntries(configurationBundle, tmp);
LOG.info("Configuring jetty with the default embedded configuration:" + "bundle: "
String tmp = DEFAULT_JETTYHOME+etcFile;
enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, tmp);
LOG.info("Configuring jetty from bundle: "
+ configurationBundle.getSymbolicName()
+ " config: "+tmp);
+ " with "+tmp);
}
if (enUrls == null || !enUrls.hasMoreElements())
{
LOG.warn("Unable to locate a jetty configuration file for " + etcFile);
throw new IllegalStateException ("Unable to locate a jetty configuration file for " + etcFile);
}
if (enUrls != null)
{
while (enUrls.hasMoreElements())
{
appendToCommaSeparatedList(res, enUrls.nextElement().toString());
URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getFileURL(enUrls.nextElement());
appendToCommaSeparatedList(res, url.toString());
}
}
}
@ -276,6 +255,8 @@ public class DefaultJettyAtJettyHomeHelper
return res.toString();
}
/* ------------------------------------------------------------ */
private static void appendToCommaSeparatedList(StringBuilder buffer, String value)
{
if (buffer.length() != 0)
@ -285,6 +266,8 @@ public class DefaultJettyAtJettyHomeHelper
buffer.append(value);
}
/* ------------------------------------------------------------ */
private static void setProperty(Dictionary<String,String> properties, String key, String value)
{
if (value != null)
@ -293,6 +276,8 @@ public class DefaultJettyAtJettyHomeHelper
}
}
/* ------------------------------------------------------------ */
/**
* recursively substitute the ${sysprop} by their actual system property.
* ${sysprop,defaultvalue} will use 'defaultvalue' as the value if no

View File

@ -23,7 +23,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
@ -31,16 +30,24 @@ import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.jetty.deploy.AppLifeCycle;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.bindings.StandardStarter;
import org.eclipse.jetty.deploy.bindings.StandardStopper;
import org.eclipse.jetty.osgi.boot.BundleContextProvider;
import org.eclipse.jetty.osgi.boot.BundleWebAppProvider;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
import org.eclipse.jetty.osgi.boot.OSGiDeployer;
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.osgi.boot.OSGiUndeployer;
import org.eclipse.jetty.osgi.boot.ServiceContextProvider;
import org.eclipse.jetty.osgi.boot.ServiceWebAppProvider;
import org.eclipse.jetty.osgi.boot.internal.jsp.TldLocatableURLClassloader;
import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.internal.webapp.LibExtClassLoaderHelper;
import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleDeployerHelper;
import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleTrackerCustomizer;
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;
@ -67,6 +74,8 @@ public class ServerInstanceWrapper
private static Logger LOG = Log.getLogger(ServerInstanceWrapper.class.getName());
private final String _managedServerName;
/**
@ -74,7 +83,7 @@ public class ServerInstanceWrapper
*/
private Server _server;
private ContextHandlerCollection _ctxtHandler;
private ContextHandlerCollection _ctxtCollection;
/**
* This is the class loader that should be the parent classloader of any
@ -85,20 +94,21 @@ public class ServerInstanceWrapper
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.
@ -110,6 +120,8 @@ public class ServerInstanceWrapper
return _commonParentClassLoaderForWebapps;
}
/* ------------------------------------------------------------ */
/**
* @return The deployment manager registered on this server.
*/
@ -118,32 +130,27 @@ public class ServerInstanceWrapper
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;
return _ctxtCollection;
}
/* ------------------------------------------------------------ */
public void start(Server server, Dictionary props) throws Exception
{
_server = server;
@ -158,20 +165,20 @@ public class ServerInstanceWrapper
List<File> shared = sharedURLs != null ? extractFiles(sharedURLs) : null;
libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader(shared, null, server, JettyBootstrapActivator.class.getClassLoader());
if (LOG.isDebugEnabled()) LOG.debug("LibExtClassLoader = "+libExtClassLoader);
Thread.currentThread().setContextClassLoader(libExtClassLoader);
configure(server, props);
init();
// now that we have an app provider we can call the registration
// customizer.
URL[] jarsWithTlds = getJarsWithTlds();
_commonParentClassLoaderForWebapps = jarsWithTlds == null ? libExtClassLoader : new TldLocatableURLClassloader(libExtClassLoader, jarsWithTlds);
if (LOG.isDebugEnabled()) LOG.debug("common classloader = "+_commonParentClassLoaderForWebapps);
server.start();
_webBundleDeployerHelper = new WebBundleDeployerHelper(this);
}
catch (Exception e)
{
@ -194,7 +201,7 @@ public class ServerInstanceWrapper
}
}
/* ------------------------------------------------------------ */
public void stop()
{
try
@ -210,6 +217,8 @@ public class ServerInstanceWrapper
}
}
/* ------------------------------------------------------------ */
/**
* TODO: right now only the jetty-jsp bundle is scanned for common taglibs.
* Should support a way to plug more bundles that contain taglibs.
@ -233,11 +242,19 @@ public class ServerInstanceWrapper
*/
private URL[] getJarsWithTlds() throws Exception
{
//Jars that are added onto the equivalent of the container classpath are:
// jstl jars: identified by the class WhenTag (and the boot-bundle manifest imports the jstl packages
// bundles identified by System property org.eclipse.jetty.osgi.tldbundles
// bundle symbolic name patterns defined in the DeploymentManager
//
// Any bundles mentioned in the Require-TldBundle manifest header of the webapp bundle MUST ALSO HAVE Import-Bundle
// in order to get them onto the classpath of the webapp.
ArrayList<URL> res = new ArrayList<URL>();
WebBundleDeployerHelper.staticInit();// that is not looking great.
for (WebappRegistrationCustomizer regCustomizer : WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS)
for (WebappRegistrationCustomizer regCustomizer : WebBundleTrackerCustomizer.JSP_REGISTRATION_HELPERS)
{
URL[] urls = regCustomizer.getJarsWithTlds(_provider, WebBundleDeployerHelper.BUNDLE_FILE_LOCATOR_HELPER);
URL[] urls = regCustomizer.getJarsWithTlds(_deploymentManager, BundleFileLocatorHelperFactory.getFactory().getHelper());
for (URL url : urls)
{
if (!res.contains(url)) res.add(url);
@ -249,6 +266,8 @@ public class ServerInstanceWrapper
return null;
}
/* ------------------------------------------------------------ */
private void configure(Server server, Dictionary props) throws Exception
{
String jettyConfigurationUrls = (String) props.get(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS);
@ -256,15 +275,20 @@ public class ServerInstanceWrapper
if (jettyConfigurations == null || jettyConfigurations.isEmpty()) { return; }
Map<String, Object> id_map = new HashMap<String, Object>();
//TODO need to put in the id of the server being configured
//Put in a mapping for the id "Server" and the name of the server as the instance being configured
id_map.put("Server", server);
id_map.put((String)props.get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME), server);
Map<String, String> properties = new HashMap<String, String>();
Enumeration<Object> en = props.keys();
while (en.hasMoreElements())
{
Object key = en.nextElement();
Object value = props.get(key);
properties.put(String.valueOf(key), String.valueOf(value));
String keyStr = String.valueOf(key);
String valStr = String.valueOf(value);
properties.put(keyStr, valStr);
server.setAttribute(keyStr, valStr);
}
for (URL jettyConfiguration : jettyConfigurations)
@ -274,6 +298,11 @@ public class ServerInstanceWrapper
{
// Execute a Jetty configuration file
Resource r = Resource.newResource(jettyConfiguration);
if (!r.exists())
{
LOG.warn("File does not exist "+r);
continue;
}
is = r.getInputStream();
XmlConfiguration config = new XmlConfiguration(is);
config.getIdMap().putAll(id_map);
@ -316,45 +345,97 @@ public class ServerInstanceWrapper
*
* It is assumed the server has already been configured with the ContextHandlerCollection structure.
*
* The server must have an instance of OSGiAppProvider. If one is not provided, it is created.
*/
private void init()
{
// Get the context handler
_ctxtHandler = (ContextHandlerCollection) _server.getChildHandlerByClass(ContextHandlerCollection.class);
_ctxtCollection = (ContextHandlerCollection) _server.getChildHandlerByClass(ContextHandlerCollection.class);
// get a deployerManager
Collection<DeploymentManager> deployers = _server.getBeans(DeploymentManager.class);
if (_ctxtCollection == null)
throw new IllegalStateException("ERROR: No ContextHandlerCollection configured in Server");
List<String> providerClassNames = new ArrayList<String>();
// get a deployerManager and some providers
List<DeploymentManager> deployers = _server.getBeans(DeploymentManager.class);
if (deployers != null && !deployers.isEmpty())
{
_deploymentManager = deployers.iterator().next();
_deploymentManager = deployers.get(0);
for (AppProvider provider : _deploymentManager.getAppProviders())
{
if (provider instanceof OSGiAppProvider)
providerClassNames.add(provider.getClass().getName());
}
}
else
{
_provider = (OSGiAppProvider) provider;
break;
//add some kind of default
_deploymentManager = new DeploymentManager();
_deploymentManager.setContexts(_ctxtCollection);
_server.addBean(_deploymentManager);
}
}
if (_provider == null)
_deploymentManager.setUseStandardBindings(false);
List<AppLifeCycle.Binding> deploymentLifeCycleBindings = new ArrayList<AppLifeCycle.Binding>();
deploymentLifeCycleBindings.add(new OSGiDeployer());
deploymentLifeCycleBindings.add(new StandardStarter());
deploymentLifeCycleBindings.add(new StandardStopper());
deploymentLifeCycleBindings.add(new OSGiUndeployer());
_deploymentManager.setLifeCycleBindings(deploymentLifeCycleBindings);
if (!providerClassNames.contains(BundleWebAppProvider.class.getName()))
{
// create it on the fly with reasonable default values.
try
{
_provider = new OSGiAppProvider();
_provider.setMonitoredDirResource(Resource.newResource(getDefaultOSGiContextsHome(new File(System.getProperty("jetty.home"))).toURI()));
BundleWebAppProvider webAppProvider = new BundleWebAppProvider(this);
_deploymentManager.addAppProvider(webAppProvider);
}
catch (IOException e)
catch (Exception e)
{
LOG.warn(e);
}
_deploymentManager.addAppProvider(_provider);
}
if (!providerClassNames.contains(ServiceWebAppProvider.class.getName()))
{
// create it on the fly with reasonable default values.
try
{
ServiceWebAppProvider webAppProvider = new ServiceWebAppProvider(this);
_deploymentManager.addAppProvider(webAppProvider);
}
catch (Exception e)
{
LOG.warn(e);
}
}
if (_ctxtHandler == null || _provider == null) throw new IllegalStateException("ERROR: No ContextHandlerCollection or OSGiAppProvider configured");
if (!providerClassNames.contains(BundleContextProvider.class.getName()))
{
try
{
BundleContextProvider contextProvider = new BundleContextProvider(this);
_deploymentManager.addAppProvider(contextProvider);
}
catch (Exception e)
{
LOG.warn(e);
}
}
if (!providerClassNames.contains(ServiceContextProvider.class.getName()))
{
try
{
ServiceContextProvider contextProvider = new ServiceContextProvider(this);
_deploymentManager.addAppProvider(contextProvider);
}
catch (Exception e)
{
LOG.warn(e);
}
}
}
/**
@ -381,10 +462,6 @@ public class ServerInstanceWrapper
return new File(jettyHome, "/contexts");
}
File getOSGiContextsHome()
{
return _provider.getContextXmlDirAsFile();
}
/**
* @return the urls in this string.
@ -398,7 +475,7 @@ public class ServerInstanceWrapper
String tok = tokenizer.nextToken();
try
{
urls.add(((DefaultFileLocatorHelper) WebBundleDeployerHelper.BUNDLE_FILE_LOCATOR_HELPER).getLocalURL(new URL(tok)));
urls.add(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(new URL(tok)));
}
catch (Throwable mfe)
{
@ -422,7 +499,7 @@ public class ServerInstanceWrapper
try
{
URL url = new URL(tok);
url = ((DefaultFileLocatorHelper) WebBundleDeployerHelper.BUNDLE_FILE_LOCATOR_HELPER).getFileURL(url);
url = BundleFileLocatorHelperFactory.getFactory().getHelper().getFileURL(url);
if (url.getProtocol().equals("file"))
{
Resource res = Resource.newResource(url);

View File

@ -0,0 +1,58 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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 org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* BundleFileLocatorHelperFactory
*
* Obtain a helper for locating files based on the bundle.
*/
public class BundleFileLocatorHelperFactory
{
private static final Logger LOG = Log.getLogger(BundleFileLocatorHelperFactory.class);
private static BundleFileLocatorHelperFactory _instance = new BundleFileLocatorHelperFactory();
private BundleFileLocatorHelperFactory() {}
public static BundleFileLocatorHelperFactory getFactory()
{
return _instance;
}
public BundleFileLocatorHelper getHelper()
{
BundleFileLocatorHelper helper = BundleFileLocatorHelper.DEFAULT;
try
{
//see if a fragment has supplied an alternative
helper = (BundleFileLocatorHelper) Class.forName(BundleFileLocatorHelper.CLASS_NAME).newInstance();
}
catch (Throwable t)
{
LOG.ignore(t);
}
return helper;
}
}

View File

@ -18,6 +18,7 @@
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;

View File

@ -20,19 +20,25 @@ package org.eclipse.jetty.osgi.boot.internal.webapp;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.jetty.osgi.boot.BundleWebAppProvider;
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.osgi.boot.ServiceProvider;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper;
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.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
@ -40,107 +46,83 @@ import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
/**
* When a {@link ContextHandler} service is activated we look into it and if the
* corresponding webapp is actually not configured then we go and register it.
* <p>
* The idea is to always go through this class when we deploy a new webapp on
* jetty.
* </p>
* <p>
* We are exposing each web-application as an OSGi service. This lets us update
* the webapps and stop/start them directly at the OSGi layer. It also give us
* many ways to declare those services: Declarative Services for example. <br/>
* It is a bit different from the way the HttpService works where we would have
* a WebappService and we woud register a webapp onto it. <br/>
* It does not go against RFC-66 nor does it prevent us from supporting the
* WebappContainer semantics.
* </p>
* JettyContextHandlerServiceTracker
*
* When a {@link ContextHandler} is activated as an osgi service we find a jetty deployer
* for it. The ContextHandler could be either a WebAppContext or any other derivative of
* ContextHandler.
*
* ContextHandlers and WebApps can also be deployed into jetty without creating them as
* osgi services. Instead, they can be deployed via manifest headers inside bundles. See
* {@link WebBundleTrackerCustomizer}.
*/
public class JettyContextHandlerServiceTracker implements ServiceListener
{
private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName());
private static Logger LOG = Log.getLogger(JettyContextHandlerServiceTracker.class);
/** New style: ability to manage multiple jetty instances */
private final IManagedJettyServerRegistry _registry;
public static final String FILTER = "(objectclass=" + ServiceProvider.class.getName() + ")";
/** 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/path/to/context/file when there is
* such thing
*/
private Map<String, ServiceReference> _indexByContextFile = new HashMap<String, ServiceReference>();
//track all instances of deployers of webapps as bundles
ServiceTracker _serviceTracker;
/** in charge of detecting changes in the osgi contexts home folder. */
private Scanner _scanner;
/* ------------------------------------------------------------ */
/**
* @param registry
*/
public JettyContextHandlerServiceTracker(IManagedJettyServerRegistry registry) throws Exception
public JettyContextHandlerServiceTracker() throws Exception
{
_registry = registry;
//track all instances of deployers of webapps
Bundle myBundle = FrameworkUtil.getBundle(this.getClass());
_serviceTracker = new ServiceTracker(myBundle.getBundleContext(), FrameworkUtil.createFilter(FILTER),null);
_serviceTracker.open();
}
public void stop() throws Exception
{
if (_scanner != null)
{
_scanner.stop();
}
// the class that created the server is also in charge of stopping it.
// 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.
* @param managedServerName
* @return
*/
protected void setupContextHomeScanner(File contextHome) throws IOException
public Map<ServiceReference, ServiceProvider> getDeployers(String managedServerName)
{
if (contextHome == null) { return; }
final String osgiContextHomeFolderCanonicalPath = contextHome.getCanonicalPath();
_scanner = new Scanner();
_scanner.setRecursive(true);
_scanner.setReportExistingFilesOnStartup(false);
_scanner.addListener(new Scanner.DiscreteListener()
if (managedServerName == null)
managedServerName = OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME;
Map<ServiceReference, ServiceProvider> candidates = new HashMap<ServiceReference, ServiceProvider>();
ServiceReference[] references = _serviceTracker.getServiceReferences();
if (references != null)
{
public void fileAdded(String filename) throws Exception
for (ServiceReference ref:references)
{
// 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);
}
});
String name = (String)ref.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
if (managedServerName.equalsIgnoreCase(name))
{
ServiceProvider candidate = (ServiceProvider)_serviceTracker.getService(ref);
if (candidate != null)
candidates.put(ref, candidate);
}
}
}
return candidates;
}
/* ------------------------------------------------------------ */
/**
* Receives notification that a service has had a lifecycle change.
*
* @param ev The <code>ServiceEvent</code> object.
*/
/**
* @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
*/
public void serviceChanged(ServiceEvent ev)
{
ServiceReference sr = ev.getServiceReference();
@ -149,16 +131,31 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
case ServiceEvent.MODIFIED:
case ServiceEvent.UNREGISTERING:
{
ContextHandler ctxtHandler = unregisterInIndex(ev.getServiceReference());
if (ctxtHandler != null && !ctxtHandler.isStopped())
BundleContext context = FrameworkUtil.getBundle(JettyBootstrapActivator.class).getBundleContext();
ContextHandler contextHandler = (ContextHandler) context.getService(sr);
//if this was not a service that another of our deployers may have deployed (in which case they will undeploy it)
String watermark = (String)sr.getProperty(OSGiWebappConstants.WATERMARK);
//Get a jetty deployer targetted to the named server instance, or the default one if not named
//The individual deployer will decide if it can remove the context or not
String serverName = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
Map<ServiceReference, ServiceProvider> candidates = getDeployers(serverName);
if (candidates != null)
{
boolean removed = false;
Iterator<Entry<ServiceReference, ServiceProvider>> itor = candidates.entrySet().iterator();
while (!removed && itor.hasNext())
{
Entry<ServiceReference, ServiceProvider> e = itor.next();
try
{
getWebBundleDeployerHelp(sr).unregister(ctxtHandler);
removed = e.getValue().serviceRemoved(sr, contextHandler);
}
catch (Exception e)
catch (Exception x)
{
__logger.warn(e);
LOG.warn("Error undeploying service representing jetty context ", x);
}
}
}
}
@ -181,210 +178,34 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
// is configured elsewhere.
return;
}
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);
if (contextPath == null)
{
contextPath = webapp.getContextPath();
}
String webXmlPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WEB_XML_PATH);
if (webXmlPath == null)
{
webXmlPath = webapp.getDescriptor();
}
String defaultWebXmlPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_DEFAULT_WEB_XML_PATH);
if (defaultWebXmlPath == null)
{
String jettyHome = System.getProperty(DefaultJettyAtJettyHomeHelper.SYS_PROP_JETTY_HOME);
if (jettyHome != null)
{
File etc = new File(jettyHome, "etc");
if (etc.exists() && etc.isDirectory())
{
File webDefault = new File(etc, "webdefault.xml");
if (webDefault.exists())
defaultWebXmlPath = webDefault.getAbsolutePath();
else
defaultWebXmlPath = webapp.getDefaultsDescriptor();
}
else
defaultWebXmlPath = webapp.getDefaultsDescriptor();
}
}
String war = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WAR);
try
{
IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr);
if (deployerHelper == null)
{
String watermark = (String)sr.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return; //one of our deployers just registered the context as an OSGi service, so we can ignore it
}
else
//Get a jetty deployer targetted to the named server instance, or the default one if not named
String serverName = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
Map<ServiceReference, ServiceProvider> candidates = getDeployers(serverName);
if (candidates != null)
{
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),
(String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE),
webXmlPath, defaultWebXmlPath, webapp);
if (handler != null)
boolean added = false;
Iterator<Entry<ServiceReference, ServiceProvider>> itor = candidates.entrySet().iterator();
while (!added && itor.hasNext())
{
registerInIndex(handler, sr);
}
}
}
catch (Throwable e)
{
__logger.warn(e);
}
}
else
{
// consider this just an empty skeleton:
if (contextFilePath == null) { throw new IllegalArgumentException("the property contextFilePath is required"); }
Entry<ServiceReference, ServiceProvider> e = itor.next();
try
{
IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr);
if (deployerHelper == null)
{
// more warnings?
added = e.getValue().serviceAdded(sr, contextHandler);
if (added && LOG.isDebugEnabled())
LOG.debug("Provider "+e.getValue()+" deployed "+contextHandler);
}
else
catch (Exception x)
{
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),
(String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE),
contextHandler);
if (handler != null)
{
registerInIndex(handler, sr);
}
}
}
catch (Throwable e)
{
__logger.warn(e);
LOG.warn("Error deploying service representing jetty context", x);
}
}
}
break;
}
}
private void registerInIndex(ContextHandler handler, ServiceReference sr)
{
_indexByServiceReference.put(sr, handler);
String key = getSymbolicNameAndContextFileKey(sr);
if (key != null)
{
_indexByContextFile.put(key, sr);
}
}
/**
* Returns the ContextHandler to stop.
*
* @param reg
* @return the ContextHandler to stop.
*/
private ContextHandler unregisterInIndex(ServiceReference sr)
{
ContextHandler handler = _indexByServiceReference.remove(sr);
String key = getSymbolicNameAndContextFileKey(sr);
if (key != null)
{
_indexByContextFile.remove(key);
}
if (handler == null)
{
// a warning?
return null;
}
return handler;
}
/**
* @param sr
* @return The key for a context file within the osgi contexts home folder.
*/
private String getSymbolicNameAndContextFileKey(ServiceReference sr)
{
String contextFilePath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
if (contextFilePath != null) { return sr.getBundle().getSymbolicName() + "/" + contextFilePath; }
return null;
}
/**
* Called by the scanner when one of the context files is changed.
*
* @param contextFileFully
*/
public void reloadJettyContextHandler(String canonicalNameOfFileChanged, String osgiContextHomeFolderCanonicalPath)
{
String key = getNormalizedRelativePath(canonicalNameOfFileChanged, osgiContextHomeFolderCanonicalPath);
if (key == null) { return; }
ServiceReference sr = _indexByContextFile.get(key);
if (sr == null)
{
// nothing to do?
return;
}
serviceChanged(new ServiceEvent(ServiceEvent.MODIFIED, sr));
}
/**
* @param canFilename
* @return
*/
private String getNormalizedRelativePath(String canFilename, String 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 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;
}
}

View File

@ -34,6 +34,7 @@ import java.util.jar.JarFile;
import javax.servlet.http.HttpServlet;
import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelper;
import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelperFactory;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
@ -86,12 +87,12 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
* @param contributor The bundle that defines this web-application.
* @throws IOException
*/
public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor, BundleClassLoaderHelper bundleClassLoaderHelper)
public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor)
throws IOException
{
super(parent, context);
_contributor = contributor;
_osgiBundleClassLoader = bundleClassLoaderHelper.getBundleClassLoader(contributor);
_osgiBundleClassLoader = BundleClassLoaderHelperFactory.getFactory().getHelper().getBundleClassLoader(contributor);
}
/**

View File

@ -19,39 +19,32 @@
package org.eclipse.jetty.osgi.boot.internal.webapp;
import java.net.URL;
import java.util.Dictionary;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.BundleProvider;
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.BundleTracker;
import org.osgi.util.tracker.BundleTrackerCustomizer;
import org.osgi.util.tracker.ServiceTracker;
/**
* Support bundles that declare the webapp directly through headers in their
* manifest.
* <p>
* Those headers will define a new WebApplication:
* <ul>
* <li>Web-ContextPath</li>
* <li>Jetty-WarFolderPath</li>
* </ul>
* </p>
* <p>
* Those headers will define a new app started via a jetty-context or a list of
* them. ',' column is the separator between the various context files.
* <ul>
* <li>Jetty-ContextFilePath</li>
* </ul>
* </p>
* And generate a jetty WebAppContext or another ContextHandler then registers
* it as service. Kind of simpler than declarative services and their xml files.
* Also avoid having the contributing bundle depend on jetty's package for
* WebApp.
* WebBundleTrackerCustomizer
*
*
* Support bundles that declare a webpp or context directly through headers in their
* manifest. They will be deployed to the default jetty Server instance.
*
* If you wish to deploy a context or webapp to a different jetty Server instance,
* register your context/webapp as an osgi service, and set the property OSGiServerConstants.MANAGED_JETTY_SERVER_NAME
* with the name of the Server instance you wish to depoy to.
*
* @author hmalphettes
*/
@ -59,6 +52,37 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
{
private static final Logger LOG = Log.getLogger(WebBundleTrackerCustomizer.class);
public static Collection<WebappRegistrationCustomizer> JSP_REGISTRATION_HELPERS = new ArrayList<WebappRegistrationCustomizer>();
public static final String FILTER = "(&(objectclass=" + BundleProvider.class.getName() + ")"+
"("+OSGiServerConstants.MANAGED_JETTY_SERVER_NAME+"="+OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME+"))";
private ServiceTracker _serviceTracker;
private BundleTracker _bundleTracker;
/* ------------------------------------------------------------ */
/**
* @throws Exception
*/
public WebBundleTrackerCustomizer ()
throws Exception
{
Bundle myBundle = FrameworkUtil.getBundle(this.getClass());
//track all instances of deployers of webapps/contexts as bundles
_serviceTracker = new ServiceTracker(myBundle.getBundleContext(), FrameworkUtil.createFilter(FILTER),null) {
public Object addingService(ServiceReference reference) {
Object object = super.addingService(reference);
LOG.debug("Deployer registered {}", reference);
openBundleTracker();
return object;
}
};
_serviceTracker.open();
}
/* ------------------------------------------------------------ */
/**
* A bundle is being added to the <code>BundleTracker</code>.
*
@ -83,8 +107,7 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
{
if (bundle.getState() == Bundle.ACTIVE)
{
boolean isWebBundle = register(bundle);
return isWebBundle ? bundle : null;
register(bundle);
}
else if (bundle.getState() == Bundle.STOPPING)
{
@ -98,6 +121,8 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
return null;
}
/* ------------------------------------------------------------ */
/**
* A bundle tracked by the <code>BundleTracker</code> has been modified.
*
@ -125,6 +150,8 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
}
}
/* ------------------------------------------------------------ */
/**
* A bundle tracked by the <code>BundleTracker</code> has been removed.
*
@ -143,129 +170,81 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
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);
if (warFolderRelativePath != null)
{
String contextPath = getWebContextPath(bundle, dic, false);
if (contextPath == null || !contextPath.startsWith("/"))
{
LOG.warn("The manifest header '" + OSGiWebappConstants.JETTY_WAR_FOLDER_PATH
+ ": "
+ warFolderRelativePath
+ "' in the bundle "
+ bundle.getSymbolicName()
+ " is not valid: there is no Web-ContextPath defined in the manifest.");
if (bundle == null)
return false;
}
// create the corresponding service and publish it in the context of
// the contributor bundle.
//It might be a bundle that we can deploy to our default jetty server instance
boolean deployed = false;
Object[] deployers = _serviceTracker.getServices();
if (deployers != null)
{
int i=0;
while (!deployed && i<deployers.length)
{
BundleProvider p = (BundleProvider)deployers[i];
try
{
JettyBootstrapActivator.registerWebapplication(bundle, warFolderRelativePath, contextPath);
return true;
deployed = p.bundleAdded(bundle);
}
catch (Throwable e)
catch (Exception x)
{
LOG.warn("Starting the web-bundle " + bundle.getSymbolicName() + " threw an exception.", e);
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)
{
String contextFileRelativePath = (String) dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
if (contextFileRelativePath == null)
{
// nothing to register here.
return false;
}
// support for multiple webapps in the same bundle:
String[] pathes = contextFileRelativePath.split(",;");
for (String path : pathes)
{
try
{
JettyBootstrapActivator.registerContext(bundle, path.trim());
}
catch (Throwable e)
{
LOG.warn(e);
}
}
return true;
}
else
{
// support for OSGi-RFC66; disclaimer, no access to the actual
// (draft) of the spec: just a couple of posts on the
// world-wide-web.
URL rfc66Webxml = bundle.getEntry("/WEB-INF/web.xml");
if (rfc66Webxml == null && dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) == null)
{
return false;// no webapp in here
}
// this is risky: should we make sure that there is no classes and
// jars directly available
// at the root of the of the bundle: otherwise they are accessible
// through the browser. we should enforce that the whole classpath
// is
// pointing to files and folders inside WEB-INF. We should
// filter-out
// META-INF too
String rfc66ContextPath = getWebContextPath(bundle, dic, rfc66Webxml == null);
try
{
JettyBootstrapActivator.registerWebapplication(bundle, ".", rfc66ContextPath);
return true;
}
catch (Throwable e)
{
LOG.warn(e);
return true;// maybe it did not work maybe it did. safer to track this bundle.
LOG.warn("Error deploying bundle for jetty context", x);
}
i++;
}
}
private String getWebContextPath(Bundle bundle, Dictionary<?, ?> dic, boolean webinfWebxmlExists)
{
String rfc66ContextPath = (String) dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (rfc66ContextPath == null)
{
if (!webinfWebxmlExists) { return null; }
// extract from the last token of the bundle's location:
// (really ?
// could consider processing the symbolic name as an alternative
// the location will often reflect the version.
// maybe this is relevant when the file is a war)
String location = bundle.getLocation();
String toks[] = location.replace('\\', '/').split("/");
rfc66ContextPath = toks[toks.length - 1];
// remove .jar, .war etc:
int lastDot = rfc66ContextPath.lastIndexOf('.');
if (lastDot != -1)
{
rfc66ContextPath = rfc66ContextPath.substring(0, lastDot);
}
}
if (!rfc66ContextPath.startsWith("/"))
{
rfc66ContextPath = "/" + rfc66ContextPath;
}
return rfc66ContextPath;
return deployed;
}
/* ------------------------------------------------------------ */
/**
* @param bundle
*/
private void unregister(Bundle bundle)
{
// nothing to do: when the bundle is stopped, each one of its service
// reference is also stopped and that is what we use to stop the
// corresponding
// webapps registered in that bundle.
Object[] deployers = _serviceTracker.getServices();
boolean undeployed = false;
if (deployers != null)
{
int i=0;
while (!undeployed && i<deployers.length)
{
try
{
undeployed = ((BundleProvider)deployers[i++]).bundleRemoved(bundle);
}
catch (Exception x)
{
LOG.warn("Error undeploying bundle for jetty context", x);
}
}
}
}
public void setAndOpenWebBundleTracker(BundleTracker bundleTracker) {
if(_bundleTracker == null) {
_bundleTracker = bundleTracker;
LOG.debug("Bundle tracker is set");
openBundleTracker();
}
}
private void openBundleTracker() {
if(_bundleTracker != null && _serviceTracker.getServices() != null &&
_serviceTracker.getServices().length > 0) {
_bundleTracker.open();
LOG.debug("Bundle tracker has been opened");
}
}
}

View File

@ -0,0 +1,61 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.utils;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* BundleClassLoaderHelperFactory
*
* Get a class loader helper adapted for the particular osgi environment.
*/
public class BundleClassLoaderHelperFactory
{
private static final Logger LOG = Log.getLogger(BundleClassLoaderHelperFactory.class);
private static BundleClassLoaderHelperFactory _instance = new BundleClassLoaderHelperFactory();
public static BundleClassLoaderHelperFactory getFactory()
{
return _instance;
}
private BundleClassLoaderHelperFactory()
{
}
public BundleClassLoaderHelper getHelper()
{
//use the default
BundleClassLoaderHelper helper = BundleClassLoaderHelper.DEFAULT;
try
{
//if a fragment has not provided their own impl
helper = (BundleClassLoaderHelper) Class.forName(BundleClassLoaderHelper.CLASS_NAME).newInstance();
}
catch (Throwable t)
{
LOG.ignore(t);
}
return helper;
}
}

View File

@ -90,4 +90,30 @@ public interface BundleFileLocatorHelper
*/
public Enumeration<URL> findEntries(Bundle bundle, String entryPath);
/**
* 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 URL getLocalURL(URL 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 URL getFileURL(URL url);
}

View File

@ -0,0 +1,91 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.utils;
import java.util.Dictionary;
import java.util.Hashtable;
import javax.security.auth.login.FailedLoginException;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
public class EventSender
{
//OSGi Event Admin events for webapps
public static final String DEPLOYING_EVENT = "org/osgi/service/web/DEPLOYING";
public static final String DEPLOYED_EVENT = "org/osgi/service/web/DEPLOYED";
public static final String UNDEPLOYING_EVENT = "org/osgi/service/web/UNDEPLOYING";
public static final String UNDEPLOYED_EVENT = "org/osgi/service/web/UNDEPLOYED";
public static final String FAILED_EVENT = "org/osgi/service/web/FAILED";
private static final EventSender __instance = new EventSender();
private Bundle _myBundle;
private EventAdmin _eventAdmin;
private EventSender ()
{
_myBundle = FrameworkUtil.getBundle(EventSender.class);
ServiceReference ref = _myBundle.getBundleContext().getServiceReference(EventAdmin.class.getName());
if (ref != null)
_eventAdmin = (EventAdmin)_myBundle.getBundleContext().getService(ref);
}
public static EventSender getInstance()
{
return __instance;
}
public void send (String topic, Bundle wab, String contextPath)
{
if (topic==null || wab==null || contextPath==null)
return;
send(topic, wab, contextPath, null);
}
public void send (String topic, Bundle wab, String contextPath, Exception ex)
{
if (_eventAdmin == null)
return;
Dictionary<String,Object> props = new Hashtable<String,Object>();
props.put("bundle.symbolicName", wab.getSymbolicName());
props.put("bundle.id", wab.getBundleId());
props.put("bundle", wab);
props.put("bundle.version", wab.getVersion());
props.put("context.path", contextPath);
props.put("timestamp", System.currentTimeMillis());
props.put("extender.bundle", _myBundle);
props.put("extender.bundle.symbolicName", _myBundle.getSymbolicName());
props.put("extender.bundle.id", _myBundle.getBundleId());
props.put("extender.bundle.version", _myBundle.getVersion());
if (FAILED_EVENT.equalsIgnoreCase(topic) && ex != null)
props.put("exception", ex);
_eventAdmin.sendEvent(new Event(topic, props));
}
}

View File

@ -0,0 +1,221 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.utils;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.osgi.framework.Bundle;
/**
* OSGiClassLoader
*
* Class loader that is aware of a bundle. Similar to WebAppClassLoader from Jetty
* and the OSGiWebAppClassLoader, but works without webapps.
*/
public class OSGiClassLoader extends URLClassLoader
{
private static final Logger LOG = Log.getLogger(OSGiClassLoader.class);
private Bundle _bundle;
private ClassLoader _osgiBundleClassLoader;
private boolean _lookInOsgiFirst = true;
private ClassLoader _parent;
/* ------------------------------------------------------------ */
public OSGiClassLoader(ClassLoader parent, Bundle bundle)
{
super(new URL[]{}, parent);
_parent = getParent();
_bundle = bundle;
_osgiBundleClassLoader = BundleClassLoaderHelperFactory.getFactory().getHelper().getBundleClassLoader(_bundle);
}
/* ------------------------------------------------------------ */
/**
* Get a resource from the classloader
*
* Copied from WebAppClassLoader
*/
public URL getResource(String name)
{
URL url= null;
boolean tried_parent= false;
if (_parent!=null && !_lookInOsgiFirst)
{
tried_parent= true;
if (_parent!=null)
url= _parent.getResource(name);
}
if (url == null)
{
url = _osgiBundleClassLoader.getResource(name);
if (url == null && name.startsWith("/"))
{
if (LOG.isDebugEnabled())
LOG.debug("HACK leading / off " + name);
url = _osgiBundleClassLoader.getResource(name.substring(1));
}
}
if (url == null && !tried_parent)
{
if (_parent!=null)
url= _parent.getResource(name);
}
if (url != null)
if (LOG.isDebugEnabled())
LOG.debug("getResource("+name+")=" + url);
return url;
}
/* ------------------------------------------------------------ */
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException
{
return loadClass(name, false);
}
/* ------------------------------------------------------------ */
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
Class<?> c = findLoadedClass(name);
ClassNotFoundException ex= null;
boolean tried_parent= false;
if (c == null && _parent!=null && !_lookInOsgiFirst)
{
tried_parent= true;
try
{
c= _parent.loadClass(name);
if (LOG.isDebugEnabled())
LOG.debug("loaded " + c);
}
catch (ClassNotFoundException e)
{
ex= e;
}
}
if (c == null)
{
try
{
c= this.findClass(name);
}
catch (ClassNotFoundException e)
{
ex= e;
}
}
if (c == null && _parent!=null && !tried_parent)
c = _parent.loadClass(name);
if (c == null)
throw ex;
if (resolve)
resolveClass(c);
if (LOG.isDebugEnabled())
LOG.debug("loaded " + c+ " from "+c.getClassLoader());
return c;
}
/* ------------------------------------------------------------ */
@Override
public Enumeration<URL> getResources(String name) throws IOException
{
Enumeration<URL> osgiUrls = _osgiBundleClassLoader.getResources(name);
Enumeration<URL> urls = super.getResources(name);
if (_lookInOsgiFirst)
{
return Collections.enumeration(toList(osgiUrls, urls));
}
else
{
return Collections.enumeration(toList(urls, osgiUrls));
}
}
/* ------------------------------------------------------------ */
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
try
{
return _lookInOsgiFirst ? _osgiBundleClassLoader.loadClass(name) : super.findClass(name);
}
catch (ClassNotFoundException cne)
{
try
{
return _lookInOsgiFirst ? super.findClass(name) : _osgiBundleClassLoader.loadClass(name);
}
catch (ClassNotFoundException cne2)
{
throw cne;
}
}
}
/* ------------------------------------------------------------ */
/**
* @param e
* @param e2
* @return
*/
private List<URL> toList(Enumeration<URL> e, Enumeration<URL> e2)
{
List<URL> list = new ArrayList<URL>();
while (e != null && e.hasMoreElements())
list.add(e.nextElement());
while (e2 != null && e2.hasMoreElements())
list.add(e2.nextElement());
return list;
}
}

View File

@ -20,7 +20,8 @@ package org.eclipse.jetty.osgi.boot.utils;
import java.net.URL;
import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
/**
* Fix various shortcomings with the way jasper parses the tld files.
@ -55,6 +56,6 @@ public interface WebappRegistrationCustomizer
* @return array of URLs
* @throws Exception
*/
URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper fileLocator) throws Exception;
URL[] getJarsWithTlds(DeploymentManager manager, BundleFileLocatorHelper fileLocator) throws Exception;
}

View File

@ -34,10 +34,6 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
private static boolean identifiedOsgiImpl = false;
private static Class BundleWiringClass = null;
private static Method BundleWiringClass_getClassLoader_method = null;
private static Method BundleClass_adapt_method = null;
private static boolean isEquinox = false;
private static boolean isFelix = false;
@ -45,26 +41,6 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
private static void init(Bundle bundle)
{
identifiedOsgiImpl = true;
try
{
BundleWiringClass = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring");
if (BundleWiringClass != null)
{
BundleWiringClass_getClassLoader_method = BundleWiringClass.getDeclaredMethod("getClassLoader", new Class[] {});
BundleClass_adapt_method = bundle.getClass().getDeclaredMethod("adapt", new Class[] { Class.class });
BundleClass_adapt_method.setAccessible(true);
return;
}
}
catch (Throwable t)
{
//nevermind: an older version of OSGi where BundleWiring is not availble
//t.printStackTrace();
}
if (!bundle.getClass().getName().startsWith("org.apache.felix"))
{
try
{
isEquinox = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost") != null;
@ -73,7 +49,6 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
{
isEquinox = false;
}
}
if (!isEquinox)
{
try
@ -95,7 +70,6 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
*/
public ClassLoader getBundleClassLoader(Bundle bundle)
{
//Older OSGi implementations:
String bundleActivator = (String) bundle.getHeaders().get("Bundle-Activator");
if (bundleActivator == null)
{
@ -119,22 +93,6 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
{
init(bundle);
}
//This works for OSGi 4.2 and more recent. Aka version 1.6
//It is using ava reflection to execute:
//(BundleClassLoader) bundle.adapt(BundleWiring.class).getClassLoader()
if (BundleClass_adapt_method != null && BundleWiringClass_getClassLoader_method != null)
{
try
{
Object bundleWiring = BundleClass_adapt_method.invoke(bundle, BundleWiringClass);
return (ClassLoader)BundleWiringClass_getClassLoader_method.invoke(bundleWiring, new Object[] {});
}
catch (Throwable t)
{
t.printStackTrace();
return null;
}
}
if (isEquinox)
{
return internalGetEquinoxBundleClassLoader(bundle);
@ -178,9 +136,6 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
private static Field Felix_ModuleImpl_m_classLoader_field;
private static Field Felix_BundleImpl_m_revisions_field;
private static ClassLoader internalGetFelixBundleClassLoader(Bundle bundle)
{
// assume felix:

View File

@ -31,13 +31,13 @@ import java.util.zip.ZipFile;
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.FileResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.FileResource;
import org.osgi.framework.Bundle;
/**
* From a bundle to its location on the filesystem.
* Often assumes the bundle is not a jar.
* From a bundle to its location on the filesystem. Assumes the bundle is not a
* jar.
*
* @author hmalphettes
*/
@ -182,16 +182,10 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
File file = new File(location.substring("file:".length()));
return file;
}
else
{
//Resort to introspection on felix:
return getBundleInstallLocationInFelix(bundle);
}
}
return null;
}
/**
* Locate a file inside a bundle.
*
@ -303,7 +297,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
*
* @return a URL to the bundle entry that uses a common protocol
*/
public static URL getLocalURL(URL url)
public URL getLocalURL(URL url)
{
if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
{
@ -339,7 +333,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
* protocol
* </p>
*/
public static URL getFileURL(URL url)
public URL getFileURL(URL url)
{
if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
{
@ -362,85 +356,4 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
return url;
}
// Felix introspection
private static Method Felix_BundleImpl_getArchive_method;
private static Method Felix_BundleArchive_getCurrentRevision_method;
private static Method Felix_BundleRevision_getRevisionRootDir_method;
private static boolean felixIntroSpectionDone = false;
/**
* Introspection of the implementation classes of Felix-3.x and Felix-4.x.
* <p>
* See org.apache.felix.framework.cache
* In pseudo code:
* <code>
* File revRootDir = BundleImpl.getArchive().getCurrentRevision().getRevisionRootDir();
* return new File(revRootDir, bundle.jar) if it exists?
* else return revRootDir
* </p>
* @param bundle
* @return The File or null if we failed to find it.
*/
private static File getBundleInstallLocationInFelix(Bundle bundle)
{
if (Felix_BundleImpl_getArchive_method == null) {
if (felixIntroSpectionDone)
{
return null;
}
felixIntroSpectionDone = true;
try
{
Felix_BundleImpl_getArchive_method = bundle.getClass().getDeclaredMethod("getArchive", new Class[] {});
Felix_BundleImpl_getArchive_method.setAccessible(true);
Object archive = Felix_BundleImpl_getArchive_method.invoke(bundle);
Class bundleArchiveClass = archive.getClass();
Felix_BundleArchive_getCurrentRevision_method = bundleArchiveClass.getDeclaredMethod("getCurrentRevision", new Class[] {});
Felix_BundleArchive_getCurrentRevision_method.setAccessible(true);
Object revision = Felix_BundleArchive_getCurrentRevision_method.invoke(archive);
Class bundleRevisionClass = revision.getClass();
Felix_BundleRevision_getRevisionRootDir_method = bundleRevisionClass.getMethod("getRevisionRootDir", new Class[] {});
Felix_BundleRevision_getRevisionRootDir_method.setAccessible(true);
}
catch (Throwable t)
{
//nevermind?
//t.printStackTrace();
Felix_BundleImpl_getArchive_method = null;
return null;
}
}
if (Felix_BundleImpl_getArchive_method != null)
{
try
{
Object archive = Felix_BundleImpl_getArchive_method.invoke(bundle);
Object revision = Felix_BundleArchive_getCurrentRevision_method.invoke(archive);
File revRootDir = (File)Felix_BundleRevision_getRevisionRootDir_method.invoke(revision);
//System.err.println("Got the archive revision root dir " + revRootDir.getAbsolutePath());
File bundleJar = new File(revRootDir, "bundle.jar");
if (bundleJar.exists())
{
//bundle.jar is hardcoded in org.apache.felix.framework.cache.JarRevision
//when it is not a bundle.jar, then the bundle location starts with 'file:' and we have already
//taken care if that scheme earlier.
return bundleJar;
}
else //sanity check?: if (new File(revRootDir, "META-INF/MANIFEST.MF").exists())
{
//this is a DirectoryRevision
return revRootDir;
}
}
catch (Throwable t)
{
//best effort: nevermind
//t.printStackTrace();
}
}
return null;
}
// -- end Felix introspection
}

View File

@ -0,0 +1,268 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.nested;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServlet;
import org.eclipse.jetty.nested.NestedConnector;
import org.eclipse.jetty.util.component.AbstractLifeCycle.AbstractLifeCycleListener;
import org.eclipse.jetty.util.component.LifeCycle;
import org.osgi.framework.FrameworkUtil;
/**
* Listens to the start and stop of the NestedConnector to register and
* unregister the NestedConnector with the BridgeServlet.
* <p>
* All interactions with the BridgeServlet are done via introspection to avoid
* depending on it directly. The BridgeServlet lives in the bootstrap-webapp;
* not inside equinox.
* </p>
*/
public class NestedConnectorListener extends AbstractLifeCycleListener
{
/**
* Name of the BridgeServlet class. By default
* org.eclipse.equinox.servletbridge.BridgeServlet
*/
private String bridgeServletClassName = "org.eclipse.equinox.servletbridge.BridgeServlet";
/**
* Name of the static method on the BridgeServlet class to register the
* servlet delegate. By default 'registerServletDelegate'
*/
private String registerServletDelegateMethodName = "registerServletDelegate";
/**
* Name of the static method on the BridgeServlet class to register the
* servlet delegate. By default 'unregisterServletDelegate'
*/
private String unregisterServletDelegateMethodName = "unregisterServletDelegate";
/**
* servlet that wraps this NestedConnector and uses the NestedConnector to
* service the requests.
*/
private NestedConnectorServletDelegate _servletDelegate;
/**
* The NestedConnector listened to.
*/
private NestedConnector nestedConnector;
/**
* @param bridgeServletClassName Name of the class that is the
* BridgeServlet. By default
* org.eclipse.equinox.servletbridge.BridgeServlet
*/
public void setBridgeServletClassName(String bridgeServletClassName)
{
this.bridgeServletClassName = bridgeServletClassName;
}
public String getBridgeServletClassName()
{
return this.bridgeServletClassName;
}
public String getRegisterServletDelegateMethodName()
{
return this.registerServletDelegateMethodName;
}
public String getUnregisterServletDelegateMethodName()
{
return this.unregisterServletDelegateMethodName;
}
/**
* @param registerServletDelegateMethodName Name of the static method on the
* BridgeServlet class to register the servlet delegate.
*/
public void setRegisterServletDelegateMethodName(String registerServletDelegateMethodName)
{
this.registerServletDelegateMethodName = registerServletDelegateMethodName;
}
/**
* @param unregisterServletDelegateMethodName Name of the static method on
* the BridgeServlet class to unregister the servlet delegate.
*/
public void setUnregisterServletDelegateMethodName(String unregisterServletDelegateMethodName)
{
this.unregisterServletDelegateMethodName = unregisterServletDelegateMethodName;
}
/**
* @param nestedConnector The NestedConnector that we are listening to here.
*/
public void setNestedConnector(NestedConnector nestedConnector)
{
this.nestedConnector = nestedConnector;
}
/**
* @return The NestedConnector that we are listening to here.
*/
public NestedConnector getNestedConnector()
{
return this.nestedConnector;
}
@Override
public void lifeCycleStarted(LifeCycle event)
{
try
{
registerWithBridgeServlet();
}
catch (Exception e)
{
if (e instanceof RuntimeException) { throw (RuntimeException) e; }
throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", e);
}
}
@Override
public void lifeCycleStopping(LifeCycle event)
{
try
{
unregisterWithBridgeServlet();
}
catch (Exception e)
{
if (e instanceof RuntimeException) { throw (RuntimeException) e; }
throw new RuntimeException("Unable to unregister the servlet delegate into the BridgeServlet.", e);
}
}
/**
* Hook into the BridgeServlet
*/
protected void registerWithBridgeServlet() throws Exception
{
_servletDelegate = new NestedConnectorServletDelegate(getNestedConnector());
try
{
invokeStaticMethod(getBridgeServletClassName(), getRegisterServletDelegateMethodName(), new Class[] { HttpServlet.class }, _servletDelegate);
}
catch (Throwable t)
{
_servletDelegate.destroy();
_servletDelegate = null;
if (t instanceof Exception) { throw (Exception) t; }
throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", t);
}
}
/**
* Unhook into the BridgeServlet
*/
protected void unregisterWithBridgeServlet() throws Exception
{
if (_servletDelegate != null)
{
try
{
invokeStaticMethod(getBridgeServletClassName(), getUnregisterServletDelegateMethodName(), new Class[] { HttpServlet.class }, _servletDelegate);
}
catch (Throwable t)
{
if (t instanceof Exception) { throw (Exception) t; }
throw new RuntimeException("Unable to unregister the servlet delegate from the BridgeServlet.", t);
}
finally
{
_servletDelegate.destroy();
_servletDelegate = null;
}
}
}
/**
*
* @param clName
* @param methName
* @param argType
* @throws Exception
*/
private static void invokeStaticMethod(String clName, String methName, Class[] argType, Object... args) throws Exception
{
Method m = getMethod(clName, methName, argType);
m.invoke(null, args);
}
/**
*
* @param clName Class that belongs to the parent classloader of the OSGi
* framework.
* @param methName Name of the method to find.
* @param argType Argument types of the method to find.
* @throws Exception
*/
private static Method getMethod(String clName, String methName, Class... argType) throws Exception
{
Class bridgeServletClass = FrameworkUtil.class.getClassLoader().loadClass(clName);
return getMethod(bridgeServletClass, methName, argType);
}
private static Method getMethod(Class cl, String methName, Class... argType) throws Exception
{
Method meth = null;
try
{
meth = cl.getMethod(methName, argType);
return meth;
}
catch (Exception e)
{
for (Method m : cl.getMethods())
{
if (m.getName().equals(methName) && m.getParameterTypes().length == argType.length)
{
int i = 0;
for (Class p : m.getParameterTypes())
{
Class ap = argType[i];
if (p.getName().equals(ap.getName()) && !p.equals(ap))
{
throw new IllegalStateException("The method \"" + m.toGenericString()
+ "\" was found. but the parameter class "
+ p.getName()
+ " is not the same "
+ " inside OSGi classloader ("
+ ap.getClassLoader()
+ ") and inside the "
+ cl.getName()
+ " classloader ("
+ p.getClassLoader()
+ ")."
+ " Are the ExtensionBundles correctly defined?");
}
}
}
}
throw e;
}
}
}

View File

@ -0,0 +1,51 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.nested;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import org.eclipse.jetty.nested.NestedConnector;
/**
* Wraps a NestedConnector into a servlet that can be plugged into
* BridgeServlet#registerServletDelegate
*/
public class NestedConnectorServletDelegate extends HttpServlet
{
private static final long serialVersionUID = 1L;
private final NestedConnector _nestedConnector;
public NestedConnectorServletDelegate(NestedConnector nestedConnector)
{
_nestedConnector = nestedConnector;
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
{
_nestedConnector.service(req, res);
}
}

View File

@ -0,0 +1,399 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
<version>9.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-jetty-osgi</artifactId>
<name>Jetty :: OSGi :: Test</name>
<description>Jetty OSGi Integration tests</description>
<properties>
<bundle-symbolic-name>${project.groupId}.boot.test.spdy</bundle-symbolic-name>
<jetty-orbit-url>http://download.eclipse.org/jetty/orbit/</jetty-orbit-url>
<assembly-directory>target/distribution</assembly-directory>
<exam.version>2.6.0</exam.version>
<url.version>1.4.0</url.version>
<paxswissbox.version>1.5.1</paxswissbox.version>
<felixversion>4.0.3</felixversion>
<injection.bundle.version>1.0</injection.bundle.version>
<runner.version>1.7.6</runner.version>
<npn-version>1.1.0.v20120525</npn-version>
</properties>
<dependencies>
<!-- Pax Exam Dependencies -->
<!-- OPS4J Swissbox Dependencies -->
<dependency>
<groupId>org.ops4j.pax.swissbox</groupId>
<artifactId>pax-swissbox-core</artifactId>
<version>${paxswissbox.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.swissbox</groupId>
<artifactId>pax-swissbox-extender</artifactId>
<version>${paxswissbox.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.swissbox</groupId>
<artifactId>pax-swissbox-lifecycle</artifactId>
<version>${paxswissbox.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.swissbox</groupId>
<artifactId>pax-swissbox-framework</artifactId>
<version>${paxswissbox.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam</artifactId>
<version>${exam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-atinject_1.0_spec</artifactId>
<version>${injection.bundle.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-inject</artifactId>
<version>${exam.version}</version>
<scope>test</scope>
</dependency>
<!-- Don't use the native container for now. Observed limitations:
- single test with a single configuration
- does not read the versions of the dependencies from the pom.xml
and hence hardcode the bundles versions in the source code instead
- no support for most configuration options for the OSGi container. -->
<!--dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-native</artifactId>
<version>${exam.version}</version>
<scope>test</scope>
</dependency-->
<!-- container is not bad but not enough config parameters yet
can't pass the VMOption for npn-boot
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-forked</artifactId>
<version>${exam.version}</version>
<scope>test</scope>
</dependency>
-->
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-paxrunner</artifactId>
<version>${exam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.runner</groupId>
<artifactId>pax-runner-no-jcl</artifactId>
<version>${runner.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-junit4</artifactId>
<version>${exam.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-link-mvn</artifactId>
<version>${exam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.url</groupId>
<artifactId>pax-url-aether</artifactId>
<version>${url.version}</version>
<scope>test</scope>
</dependency>
<!-- OSGi R4 frameworks -->
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.framework</artifactId>
<version>${felixversion}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-testforge</artifactId>
<version>${exam.version}</version>
<scope>test</scope>
</dependency>
<!-- For sane logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
<!-- Orbit Servlet Deps -->
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.servlet</artifactId>
<scope>test</scope>
</dependency>
<!-- Orbit JSP Deps -->
<!-- OSGi Deps -->
<dependency>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-boot</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-boot-jsp</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-httpservice</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<!-- Jetty Deps -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-deploy</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-xml</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-api</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-common</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-client</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-servlet</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-server</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-core</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-server</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-http-server</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-client</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty.npn</groupId>
<artifactId>npn-boot</artifactId>
<version>${npn-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-plus</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Eclipse OSGi Deps -->
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
<scope>runtime</scope>
<exclusions>
<exclusion>
<!-- we use the servlet jar from orbit -->
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.equinox.http</groupId>
<artifactId>servlet</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>test-jetty-webapp</artifactId>
<version>${project.version}</version>
<classifier>webbundle</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- No point defining -Xbootclasspath as the actual OSGi VM is run as a forked process by pax-exam -->
<!--argLine>-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn-version}/npn-boot-${npn-version}.jar</argLine-->
<!-- But we do pass the sys property of the npn-boot jar -->
<argLine>-Dmortbay-npn-boot=${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn-version}/npn-boot-${npn-version}.jar</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.servicemix.tooling</groupId>
<artifactId>depends-maven-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<id>generate-depends-file</id>
<goals>
<goal>generate-depends-file</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.apache.servicemix.tooling</groupId>
<artifactId>depends-maven-plugin</artifactId>
<versionRange>[1.2,)</versionRange>
<goals>
<goal>generate-depends-file</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@ -0,0 +1,2 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.spdy.LEVEL=WARN

View File

@ -0,0 +1,40 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Configure the deployment manager -->
<!-- =========================================================== -->
<Call name="addBean">
<Arg>
<New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
<Set name="contexts">
<Ref refid="Contexts" />
</Set>
<Call name="setContextAttribute">
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
<Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
</Call>
<!-- Providers of OSGi Apps -->
<Call name="addAppProvider">
<Arg>
<New class="org.eclipse.jetty.osgi.boot.OSGiAppProvider">
<!--
<Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
-->
<Set name="scanInterval">0</Set>
<Set name="contextXmlDir"><Property name="jetty.home" default="." />/contexts</Set>
<!-- comma separated list of bundle symbolic names that contain custom tag libraries (*.tld files) -->
<!-- if those bundles don't exist or can't be loaded no errors or warning will be issued! -->
<!-- This default value plugs in the tld files of the reference implementation of JSF -->
<Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldbundles" default="javax.faces.jsf-impl" /></Set>
</New>
</Arg>
</Call>
</New>
</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,22 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Add connector -->
<!-- =========================================================== -->
<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.ServerConnector">
<Arg><Ref refid="Server" /></Arg>
<Set name="host"><Property name="jetty.host" /></Set>
<Set name="port"><Property name="jetty.port" default="8080"/></Set>
<Set name="idleTimeout">300000</Set>
</New>
</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,157 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- HttpChannel Configuration -->
<!-- =========================================================== -->
<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
<Set name="secureScheme">https</Set>
<Set name="securePort">
<SystemProperty name="jetty.spdy.port" default="8443"/>
</Set>
<Set name="outputBufferSize">32768</Set>
<Set name="requestHeaderSize">8192</Set>
<Set name="responseHeaderSize">8192</Set>
<Call name="addCustomizer">
<Arg>
<New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/>
</Arg>
</Call>
</New>
<!-- =========================================================== -->
<!-- Setup a SSL Context factory -->
<!-- =========================================================== -->
<New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
<Set name="KeyStorePath"><Property name="jetty.home" default="."/>/etc/keystore
</Set>
<Set name="KeyStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
<Set name="KeyManagerPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
<Set name="TrustStorePath"><Property name="jetty.home" default="."/>/etc/keystore
</Set>
<Set name="TrustStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
</New>
<!-- =========================================================== -->
<!-- Add HTTP Customizer for Secure request -->
<!-- =========================================================== -->
<Ref refid="httpConfig">
<Call name="addCustomizer">
<Arg>
<New class="org.eclipse.jetty.server.SecureRequestCustomizer"/>
</Arg>
</Call>
</Ref>
<!-- =========================================================== -->
<!-- Create a push strategy which can be used by reference by -->
<!-- individual connection factories below. -->
<!-- -->
<!-- Consult the javadoc of o.e.j.spdy.server.http.ReferrerPushStrategy -->
<!-- for all configuration that may be set here. -->
<!-- =========================================================== -->
<New id="pushStrategy" class="org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy">
<!-- Uncomment to blacklist browsers for this push strategy. If one of the blacklisted Strings occurs in the
user-agent header sent by the client, push will be disabled for this browser. This is case insensitive" -->
<!--
<Set name="UserAgentBlacklist">
<Array type="String">
<Item>.*(?i)firefox/14.*</Item>
<Item>.*(?i)firefox/15.*</Item>
<Item>.*(?i)firefox/16.*</Item>
</Array>
</Set>
-->
<!-- Uncomment to override default file extensions to push -->
<!--
<Set name="PushRegexps">
<Array type="String">
<Item>.*\.css</Item>
<Item>.*\.js</Item>
<Item>.*\.png</Item>
<Item>.*\.jpg</Item>
<Item>.*\.gif</Item>
</Array>
</Set>
-->
<Set name="referrerPushPeriod">5000</Set>
<Set name="maxAssociatedResources">32</Set>
</New>
<!-- =========================================================== -->
<!-- Set connectors -->
<!-- =========================================================== -->
<Call id="sslConnector" name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.ServerConnector">
<Arg name="server">
<Ref refid="Server"/>
</Arg>
<Arg name="factories">
<Array type="org.eclipse.jetty.server.ConnectionFactory">
<Item>
<New class="org.eclipse.jetty.server.SslConnectionFactory">
<Arg name="next">npn</Arg>
<Arg name="sslContextFactory">
<Ref refid="sslContextFactory"/>
</Arg>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.spdy.server.NPNServerConnectionFactory">
<Arg name="protocols">
<Array type="String">
<Item>spdy/3</Item>
<Item>spdy/2</Item>
<Item>http/1.1</Item>
</Array>
</Arg>
<Set name="defaultProtocol">http/1.1</Set>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
<Arg name="version" type="int">3</Arg>
<Arg name="config">
<Ref refid="httpConfig"/>
</Arg>
<!-- <Arg name="pushStrategy"><Ref refid="pushStrategy"/></Arg> -->
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
<Arg name="version" type="int">2</Arg>
<Arg name="config">
<Ref refid="httpConfig"/>
</Arg>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.server.HttpConnectionFactory">
<Arg name="config">
<Ref refid="httpConfig"/>
</Arg>
</New>
</Item>
</Array>
</Arg>
<Set name="host">
<Property name="jetty.host"/>
</Set>
<Set name="port">
<SystemProperty name="jetty.spdy.port" default="8443"/>
</Set>
<Set name="idleTimeout">30000</Set>
</New>
</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,20 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Configure Authentication Login Service -->
<!-- Realms may be configured for the entire server here, or -->
<!-- they can be configured for a specific web app in a context -->
<!-- configuration (see $(jetty.home)/webapps/test.xml for an -->
<!-- example). -->
<!-- =========================================================== -->
<Call name="addBean">
<Arg>
<New class="org.eclipse.jetty.security.HashLoginService">
<Set name="name">Test Realm</Set>
<Set name="config"><Property name="jetty.home" default="src/test/config"/>realm.properties</Set>
<Set name="refreshInterval">0</Set>
</New>
</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,69 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<!-- =============================================================== -->
<!-- Configure the Jetty Server -->
<!-- -->
<!-- Documentation of this file format can be found at: -->
<!-- http://wiki.eclipse.org/Jetty/Reference/jetty.xml_syntax -->
<!-- =============================================================== -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Server Thread Pool -->
<!-- =========================================================== -->
<Arg name="threadPool">
<!-- Default queued blocking threadpool -->
<New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
<Set name="minThreads">10</Set>
<Set name="maxThreads">200</Set>
</New>
</Arg>
<!-- =========================================================== -->
<!-- Set handler Collection Structure -->
<!-- =========================================================== -->
<Set name="handler">
<New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
<Set name="handlers">
<Array type="org.eclipse.jetty.server.Handler">
<Item>
<New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
</Item>
<Item>
<New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
</Item>
<Item>
<New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
</Item>
</Array>
</Set>
</New>
</Set>
<!-- =========================================================== -->
<!-- extra options -->
<!-- =========================================================== -->
<Set name="stopAtShutdown">true</Set>
<Set name="stopTimeout">1000</Set>
<Set name="dumpAfterStart">false</Set>
<Set name="dumpBeforeStop">false</Set>
<!-- =========================================================== -->
<!-- jetty-jndi by default -->
<!-- =========================================================== -->
<Call class="java.lang.System" name="setProperty">
<Arg>java.naming.factory.initial</Arg>
<Arg><Property name="java.naming.factory.initial" default="org.eclipse.jetty.jndi.InitialContextFactory"/></Arg>
</Call>
<Call class="java.lang.System" name="setProperty">
<Arg>java.naming.factory.url.pkgs</Arg>
<Arg><Property name="java.naming.factory.url.pkgs" default="org.eclipse.jetty.jndi"/></Arg>
</Call>
</Configure>

View File

@ -0,0 +1,21 @@
#
# This file defines users passwords and roles for a HashUserRealm
#
# The format is
# <username>: <password>[,<rolename> ...]
#
# Passwords may be clear text, obfuscated or checksummed. The class
# org.eclipse.util.Password should be used to generate obfuscated
# passwords or password checksums
#
# If DIGEST Authentication is used, the password must be in a recoverable
# format, either plain text or OBF:.
#
jetty: MD5:164c88b302622e17050af52c89945d44,user
admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin
other: OBF:1xmk1w261u9r1w1c1xmq,user
plain: plain,user
user: password,user
# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password
digest: MD5:6e120743ad67abfbc385bc2bb754e297,user

View File

@ -0,0 +1,195 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.test;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.Assert;
import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Option;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpService;
/**
* Helper methods for pax-exam tests
*/
public class AbstractTestOSGi
{
private Map<String,Bundle> _bundles;
/**
* Note: this will run many more tests.
* TODO: find a better way to control this and use non-deprecated methods.
* @param options
*/
protected static void addMoreOSGiContainers(List<Option> options)
{
options.add(CoreOptions.equinox().version("3.6.1"));
options.add(CoreOptions.equinox().version("3.7.0"));
options.add(CoreOptions.felix().version("3.2.2"));
options.add(CoreOptions.felix().version("4.0.2"));
}
protected Bundle getBundle(BundleContext bundleContext, String symbolicName)
{
if (_bundles == null)
{
_bundles = new HashMap<String,Bundle>();
for (Bundle b : bundleContext.getBundles())
{
Bundle prevBundle = _bundles.put(b.getSymbolicName(), b);
String err = prevBundle != null ? "2 versions of the bundle " + b.getSymbolicName() +
" " + b.getHeaders().get("Bundle-Version") +
" and " + prevBundle.getHeaders().get("Bundle-Version")
: "";
Assert.assertNull(err, prevBundle);
}
}
return _bundles.get(symbolicName);
}
protected void assertActiveBundle(BundleContext bundleContext, String symbolicName) throws Exception
{
Bundle b = getBundle(bundleContext, symbolicName);
Assert.assertNotNull(b);
Assert.assertEquals(b.getSymbolicName()+" must be active.", Bundle.ACTIVE, b.getState());
}
protected void assertActiveOrResolvedBundle(BundleContext bundleContext, String symbolicName) throws Exception
{
Bundle b = getBundle(bundleContext, symbolicName);
Assert.assertNotNull(b);
if (b.getHeaders().get("Fragment-Host") == null) diagnoseNonActiveOrNonResolvedBundle(b);
Assert.assertTrue(b.getSymbolicName()+" must be active or resolved. It was "+b.getState(),
b.getState() == Bundle.ACTIVE || b.getState() == Bundle.RESOLVED);
}
protected void assertAllBundlesActiveOrResolved(BundleContext bundleContext)
{
for (Bundle b : bundleContext.getBundles())
{
if (b.getState() == Bundle.INSTALLED)
{
diagnoseNonActiveOrNonResolvedBundle(b);
}
Assert.assertTrue("Bundle: " + b + " (state should be " +
"ACTIVE[" + Bundle.ACTIVE + "] or RESOLVED[" + Bundle.RESOLVED + "]" +
", but was [" + b.getState() + "])",
(b.getState() == Bundle.ACTIVE) || (b.getState() == Bundle.RESOLVED));
}
}
protected boolean diagnoseNonActiveOrNonResolvedBundle(Bundle b)
{
if (b.getState() != Bundle.ACTIVE && b.getHeaders().get("Fragment-Host") == null)
{
try
{
System.err.println("Trying to start the bundle "+b.getSymbolicName()+
" that was supposed to be active or resolved.");
b.start();
System.err.println(b.getSymbolicName() + " did start");
return true;
}
catch (Throwable t)
{
System.err.println(b.getSymbolicName() + " failed to start");
t.printStackTrace(System.err);
return false;
}
}
System.err.println(b.getSymbolicName() + " was already started");
return false;
}
protected void debugBundles(BundleContext bundleContext)
{
Map<String,Bundle> bundlesIndexedBySymbolicName = new HashMap<String, Bundle>();
System.err.println("Active " + Bundle.ACTIVE);
System.err.println("RESOLVED " + Bundle.RESOLVED);
System.err.println("INSTALLED " + Bundle.INSTALLED);
for( Bundle b : bundleContext.getBundles() )
{
bundlesIndexedBySymbolicName.put(b.getSymbolicName(), b);
System.err.println(" " + b.getSymbolicName() + " " + b.getState());
}
}
protected SslContextFactory getSslContextFactory()
{
SslContextFactory sslContextFactory = new SslContextFactory(true);
sslContextFactory.setEndpointIdentificationAlgorithm("");
return sslContextFactory;
}
protected void testHttpServiceGreetings(BundleContext bundleContext, String protocol, int port) throws Exception
{
assertActiveBundle(bundleContext, "org.eclipse.jetty.osgi.boot");
assertActiveBundle(bundleContext, "org.eclipse.jetty.osgi.httpservice");
assertActiveBundle(bundleContext, "org.eclipse.equinox.http.servlet");
//in the OSGi world this would be bad code and we should use a bundle tracker.
//here we purposely want to make sure that the httpService is actually ready.
ServiceReference sr = bundleContext.getServiceReference(HttpService.class.getName());
Assert.assertNotNull("The httpServiceOSGiBundle is started and should " +
"have deployed a service reference for HttpService" ,sr);
HttpService http = (HttpService)bundleContext.getService(sr);
http.registerServlet("/greetings", new HttpServlet() {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException,
IOException {
resp.getWriter().write("Hello");
}
}, null, null);
//now test the servlet
HttpClient client = protocol.equals("https") ? new HttpClient(getSslContextFactory()) : new HttpClient();
try
{
client.start();
ContentResponse response = client.GET(protocol+"://127.0.0.1:"+port+"/greetings");
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
String content = new String(response.getContent());
Assert.assertEquals("Hello", content);
}
finally
{
client.stop();
}
}
}

View File

@ -0,0 +1,122 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.test;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.osgi.framework.BundleContext;
/**
* Default OSGi setup integration test
*/
@RunWith( JUnit4TestRunner.class )
public class TestJettyOSGiBootCore extends AbstractTestOSGi {
public static int DEFAULT_JETTY_HTTP_PORT = 9876;
@Inject
private BundleContext bundleContext;
@Configuration
public Option[] config()
{
ArrayList<Option> options = new ArrayList<Option>();
addMoreOSGiContainers(options);
options.addAll(provisionCoreJetty());
options.add(CoreOptions.junitBundles());
options.addAll(httpServiceJetty());
return options.toArray(new Option[options.size()]);
}
public static List<Option> provisionCoreJetty()
{
List<Option> res = new ArrayList<Option>();
// get the jetty home config from the osgi boot bundle.
res.add(CoreOptions.systemProperty("jetty.port").value(String.valueOf(DEFAULT_JETTY_HTTP_PORT)));
res.add(CoreOptions.systemProperty("jetty.home.bundle").value("org.eclipse.jetty.osgi.boot"));
res.addAll(coreJettyDependencies());
return res;
}
public static List<Option> coreJettyDependencies()
{
List<Option> res = new ArrayList<Option>();
res.add(mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot" ).versionAsInProject().start());
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-deploy" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-server" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-servlet" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-util" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-http" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-xml" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-webapp" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-io" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-continuation" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-security" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-servlets" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-client" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-api" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-common" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-servlet" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-server" ).versionAsInProject().noStart());
return res;
}
public static List<Option> httpServiceJetty()
{
List<Option> res = new ArrayList<Option>();
res.add(mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-httpservice" ).versionAsInProject().start());
res.add(mavenBundle().groupId( "org.eclipse.equinox.http" ).artifactId( "servlet" ).versionAsInProject().start());
return res;
}
@Test
public void assertAllBundlesActiveOrResolved()
{
assertAllBundlesActiveOrResolved(bundleContext);
}
/**
* You will get a list of bundles installed by default
* plus your testcase, wrapped into a bundle called pax-exam-probe
*/
@Test
public void testHttpService() throws Exception
{
testHttpServiceGreetings(bundleContext, "http", DEFAULT_JETTY_HTTP_PORT);
}
}

View File

@ -0,0 +1,122 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.test;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.osgi.framework.BundleContext;
/**
* SPDY setup.
*/
@RunWith( JUnit4TestRunner.class )
public class TestJettyOSGiBootSpdy extends AbstractTestOSGi {
private static final String JETTY_SPDY_PORT = "jetty.spdy.port";
private static final int DEFAULT_JETTY_SPDY_PORT = 9877;
@Inject
private BundleContext bundleContext;
@Configuration
public Option[] config()
{
ArrayList<Option> options = new ArrayList<Option>();
addMoreOSGiContainers(options);
options.addAll(TestJettyOSGiBootCore.provisionCoreJetty());
options.addAll(TestJettyOSGiBootWithJsp.configureJettyHomeAndPort("jetty-spdy.xml"));
options.add(CoreOptions.junitBundles());
options.addAll(TestJettyOSGiBootCore.httpServiceJetty());
options.addAll(spdyJettyDependencies());
return options.toArray(new Option[options.size()]);
}
public static List<Option> spdyJettyDependencies()
{
List<Option> res = new ArrayList<Option>();
res.add(CoreOptions.systemProperty(JETTY_SPDY_PORT).value(String.valueOf(DEFAULT_JETTY_SPDY_PORT)));
//java -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn-version}/npn-boot-${npn-version}.jar
// res.add(CoreOptions.vmOptions("-Xbootclasspath/p:"+System.getenv("HOME")+"/.m2/repository/org/mortbay/jetty/npn/npn-boot/"+npnBootVersion+"/npn-boot-"+npnBootVersion+".jar"));
String npnBoot = System.getProperty("mortbay-npn-boot");
if (npnBoot == null)
{
throw new IllegalStateException("Please define the path to the npn boot jar as the sys property -Dmortbay-npn-boot");
//are we trying to be too nice? this kinds of work outside of maven maybe
// String npnBootUrl = mavenBundle().groupId( "org.mortbay.jetty.npn" ).artifactId( "npn-boot" ).versionAsInProject().getURL();
// String npnBootVersion = npnBootUrl.split("\\/")[2];
// if (!Character.isDigit(npnBootVersion.charAt(0)))
// {
// throw new IllegalArgumentException(npnBootUrl + " - " + npnBootVersion);
// }
// npnBoot = System.getenv("HOME")+"/.m2/repository/org/mortbay/jetty/npn/npn-boot/"+npnBootVersion+"/npn-boot-"+npnBootVersion+".jar";
}
File checkNpnBoot = new File(npnBoot);
if (!checkNpnBoot.exists())
{
throw new IllegalStateException("Unable to find the npn boot jar here: " + npnBoot);
}
res.add(CoreOptions.vmOptions("-Xbootclasspath/p:"+npnBoot));
res.add(CoreOptions.bootDelegationPackages("org.eclipse.jetty.npn"));
res.add(mavenBundle().groupId( "org.eclipse.jetty.spdy" ).artifactId( "spdy-core" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.spdy" ).artifactId( "spdy-server" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.spdy" ).artifactId( "spdy-http-server" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.spdy" ).artifactId( "spdy-client" ).versionAsInProject().noStart());
return res;
}
@Test
public void checkNpnBootOnBootstrapClasspath() throws Exception
{
Class<?> npn = Thread.currentThread().getContextClassLoader()
.loadClass("org.eclipse.jetty.npn.NextProtoNego");
Assert.assertNotNull(npn);
Assert.assertNull(npn.getClassLoader());
}
@Test
public void assertAllBundlesActiveOrResolved()
{
assertAllBundlesActiveOrResolved(bundleContext);
}
@Test
public void testSpdyOnHttpService() throws Exception
{
testHttpServiceGreetings(bundleContext, "https", DEFAULT_JETTY_SPDY_PORT);
}
}

View File

@ -0,0 +1,177 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.test;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.options;
import static org.ops4j.pax.exam.CoreOptions.systemProperty;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject;
import junit.framework.Assert;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.osgi.framework.BundleContext;
/**
* Pax-Exam to make sure the jetty-osgi-boot can be started along with the httpservice web-bundle.
* Then make sure we can deploy an OSGi service on the top of this.
*/
@RunWith( JUnit4TestRunner.class )
public class TestJettyOSGiBootWithJsp extends AbstractTestOSGi
{
private static final boolean LOGGING_ENABLED = true;
private static final boolean REMOTE_DEBUGGING = false;
@Inject
BundleContext bundleContext = null;
@Configuration
public static Option[] configure()
{
ArrayList<Option> options = new ArrayList<Option>();
addMoreOSGiContainers(options);
options.add(CoreOptions.junitBundles());
options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*",
"org.w3c.*", "javax.xml.*"));
options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
// Enable Logging
if(LOGGING_ENABLED) {
options.addAll(Arrays.asList(options(
// install log service using pax runners profile abstraction (there are more profiles, like DS)
// logProfile(),
// this is how you set the default log level when using pax logging (logProfile)
systemProperty( "org.ops4j.pax.logging.DefaultServiceLog.level" ).value( "INFO" )
)));
}
options.addAll(jspDependencies());
// Remote JDWP Debugging, this won't work with the forked container.
// if(REMOTE_DEBUGGING) {
// options.addAll(Arrays.asList(options(
// // this just adds all what you write here to java vm argumenents of the (new) osgi process.
// PaxRunnerOptions.vmOption( "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5006" )
// )));
// }
//bug at the moment: this would make the httpservice catch all
//requests and prevent the webapp at the root context to catch any of them.
//options.addAll(TestJettyOSGiBootCore.httpServiceJetty());
return options.toArray(new Option[options.size()]);
}
public static List<Option> configureJettyHomeAndPort(String jettySelectorFileName)
{
File etcFolder = new File("src/test/config/etc");
String etc = "file://" + etcFolder.getAbsolutePath();
List<Option> options = new ArrayList<Option>();
options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS)
.value(etc + "/jetty.xml;" +
etc + "/" + jettySelectorFileName + ";" +
etc + "/jetty-deployer.xml;" +
etc + "/jetty-testrealm.xml"));
options.add(systemProperty("jetty.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT)));
options.add(systemProperty("jetty.home").value(etcFolder.getParentFile().getAbsolutePath()));
return options;
}
public static List<Option> jspDependencies() {
List<Option> res = new ArrayList<Option>();
/* orbit deps */
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet.jsp" ).versionAsInProject());
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet.jsp.jstl" ).versionAsInProject());
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.el" ).versionAsInProject());
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "com.sun.el" ).versionAsInProject());
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.apache.jasper.glassfish" ).versionAsInProject());
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.apache.taglibs.standard.glassfish" ).versionAsInProject());
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.eclipse.jdt.core" ).versionAsInProject());
/* jetty-osgi deps */
res.add(mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot-jsp" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "test-jetty-webapp" ).classifier("webbundle").versionAsInProject());
return res;
}
@Test
public void assertAllBundlesActiveOrResolved()
{
assertAllBundlesActiveOrResolved(bundleContext);
}
//at the moment can't run httpservice with jsp at the same time.
//that is a regression in jetty-9
@Ignore
@Test
public void testHttpService() throws Exception
{
super.testHttpServiceGreetings(bundleContext, "http", TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT);
}
@Test
public void testJspDump() throws Exception
{
//System.err.println("http://127.0.0.1:9876/jsp/dump.jsp sleeping....");
//Thread.currentThread().sleep(5000000);
//now test the jsp/dump.jsp
HttpClient client = new HttpClient();
try
{
client.start();
ContentResponse response = client.GET("http://127.0.0.1:"+
TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT+"/jsp/dump.jsp");
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
String content = new String(response.getContent());
//System.err.println("content: " + content);
Assert.assertTrue(content.contains("<tr><th>ServletPath:</th><td>/jsp/dump.jsp</td></tr>"));
}
finally
{
client.stop();
}
}
}

View File

@ -23,6 +23,8 @@
<module>jetty-osgi-boot-jsp</module>
<module>jetty-osgi-boot-warurl</module>
<module>jetty-osgi-httpservice</module>
<module>test-jetty-osgi-webapp</module>
<module>test-jetty-osgi-context</module>
<module>test-jetty-osgi</module>
</modules>
<build>
@ -142,28 +144,6 @@
<artifactId>servlet</artifactId>
<version>${equinox-http-servlet-version}</version>
</dependency>
<!--dependency>
<groupId>org.eclipse.equinox</groupId>
<artifactId>servletbridge</artifactId>
<version>${equinox-servletbridge-version}</version>
</dependency-->
<!-- not ready <dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-impl</artifactId>
<version>${jsp-impl-2.2-glassfish-version}</version>
</dependency-->
<!--
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-2.1-glassfish</artifactId>
<version>${jsp-2.1-glassfish-version}</version>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-api-2.1-glassfish</artifactId>
<version>${jsp-2.1-glassfish-version}</version>
</dependency>
-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>

View File

@ -0,0 +1,113 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
<version>9.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-jetty-osgi-context</artifactId>
<name>Jetty :: OSGi :: Context</name>
<description>Test Jetty OSGi bundle with a ContextHandler</description>
<properties>
<bundle-symbolic-name>${project.groupId}.testcontext</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/context</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>artifact-jar</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
<execution>
<id>test-jar</id>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.osgi.testcontext;singleton:=true</Bundle-SymbolicName>
<Bundle-Name>Jetty OSGi Test Context</Bundle-Name>
<Bundle-Activator>com.acme.osgi.Activator</Bundle-Activator>
<Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
<!-- disable the uses directive: jetty will accomodate pretty much any versions
of the packages it uses; no need to reflect some tight dependency determined at
compilation time. -->
<_nouses>true</_nouses>
<Import-Package>
javax.servlet;version="2.6.0",
javax.servlet.resources;version="2.6.0",
org.osgi.framework,
org.osgi.service.cm;version="1.2.0",
org.osgi.service.packageadmin,
org.osgi.service.startlevel;version="1.0.o",
org.osgi.service.url;version="1.0.0",
org.osgi.util.tracker;version="1.3.0",
org.slf4j;resolution:=optional,
org.slf4j.spi;resolution:=optional,
org.slf4j.helpers;resolution:=optional,
org.xml.sax,
org.xml.sax.helpers,
*
</Import-Package>
<DynamicImport-Package>org.eclipse.jetty.*;version="[9.0,10.0)"</DynamicImport-Package>
<!--Require-Bundle/-->
<!-- Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment -->
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
<!-- Get root for static content, could be on file system or this bundle -->
<Call id="res" class="org.eclipse.jetty.util.resource.Resource" name="newResource">
<Arg><Property name="bundle.root"/></Arg>
</Call>
<Ref id="res">
<Call id="base" name="addPath">
<Arg>/static/</Arg>
</Call>
</Ref>
<Set name="contextPath">/unset</Set>
<!-- Set up the base resource for static files relative to inside bundle -->
<Set name="baseResource">
<Ref id="base"/>
</Set>
<Set name="handler">
<New class="org.eclipse.jetty.server.handler.ResourceHandler">
<Set name="welcomeFiles">
<Array type="String">
<Item>index.html</Item>
</Array>
</Set>
<Set name="cacheControl">max-age=3600,public</Set>
</New>
</Set>
</Configure>

View File

@ -0,0 +1,91 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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 com.acme.osgi;
import java.util.Dictionary;
import java.util.Hashtable;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.BundleTracker;
/**
* Bootstrap a ContextHandler
*
*
*/
public class Activator implements BundleActivator
{
/**
*
* @param context
*/
public void start(final BundleContext context) throws Exception
{
ContextHandler ch = new ContextHandler();
ch.addEventListener(new ServletContextListener () {
@Override
public void contextInitialized(ServletContextEvent sce)
{
System.err.println("Context is initialized");
try
{
Class clazz = Thread.currentThread().getContextClassLoader().loadClass("org.eclipse.jetty.npn.NextProtoNego");
System.err.println("Classloader for org.eclipse.jetty.npn.NextProtoNego="+clazz.getClassLoader());
}
catch (Exception e)
{
e.printStackTrace();
}
}
@Override
public void contextDestroyed(ServletContextEvent sce)
{
System.err.println("CONTEXT IS DESTROYED!");
}
});
Dictionary props = new Hashtable();
props.put("contextPath","/acme");
props.put("Jetty-ContextFilePath", "acme.xml");
context.registerService(ContextHandler.class.getName(),ch,props);
}
/**
* Stop the activator.
*
* @see
* org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception
{
}
}

View File

@ -0,0 +1,6 @@
<html>
<body>
<h1>Test OSGi Context</h1>
<p>ContextHandler registered as a service successfully deployed.</p>
</body>
</html>

View File

@ -0,0 +1,107 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
<version>9.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-jetty-osgi-webapp</artifactId>
<name>Jetty :: OSGi :: WebApp</name>
<description>Test Jetty OSGi Webapp bundle</description>
<properties>
<bundle-symbolic-name>${project.groupId}.webapp</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>artifact-jar</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
<execution>
<id>test-jar</id>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.osgi.testapp;singleton:=true</Bundle-SymbolicName>
<Bundle-Name>Jetty OSGi Test WebApp</Bundle-Name>
<Bundle-Activator>com.acme.osgi.Activator</Bundle-Activator>
<Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
<!-- disable the uses directive: jetty will accomodate pretty much any versions
of the packages it uses; no need to reflect some tight dependency determined at
compilation time. -->
<_nouses>true</_nouses>
<Import-Package>
org.osgi.framework,
org.osgi.service.cm;version="1.2.0",
org.osgi.service.packageadmin,
org.osgi.service.startlevel;version="1.0.o",
org.osgi.service.url;version="1.0.0",
org.osgi.util.tracker;version="1.3.0",
org.slf4j;resolution:=optional,
org.slf4j.spi;resolution:=optional,
org.slf4j.helpers;resolution:=optional,
org.xml.sax,
org.xml.sax.helpers,
*
</Import-Package>
<DynamicImport-Package>org.eclipse.jetty.*;version="[9.0,10.0)"</DynamicImport-Package>
<!--Require-Bundle/-->
<!-- Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment -->
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,66 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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 com.acme.osgi;
import java.util.Dictionary;
import java.util.Hashtable;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
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.BundleException;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.BundleTracker;
/**
* Bootstrap a webapp
*
*
*/
public class Activator implements BundleActivator
{
/**
*
* @param context
*/
public void start(BundleContext context) throws Exception
{
WebAppContext webapp = new WebAppContext();
Dictionary props = new Hashtable();
props.put("war",".");
props.put("contextPath","/acme");
//uiProps.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, serverName);
context.registerService(ContextHandler.class.getName(),webapp,props);
}
/**
* Stop the activator.
*
* @see
* org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception
{
}
}

View File

@ -0,0 +1,6 @@
<html>
<body>
<h1>Test OSGi WebApp</h1>
<p>Webapp registered by bundle as service successfully deployed.</p>
</body>
</html>

View File

@ -16,23 +16,6 @@
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
<Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
</Call>
<!-- Providers of OSGi Apps -->
<Call name="addAppProvider">
<Arg>
<New class="org.eclipse.jetty.osgi.boot.OSGiAppProvider">
<!--
<Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
-->
<Set name="scanInterval">0</Set>
<Set name="contextXmlDir"><Property name="jetty.home" default="." />/contexts</Set>
<!-- comma separated list of bundle symbolic names that contain custom tag libraries (*.tld files) -->
<!-- if those bundles don't exist or can't be loaded no errors or warning will be issued! -->
<!-- This default value plugs in the tld files of the reference implementation of JSF -->
<Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldbundles" default="javax.faces.jsf-impl" /></Set>
</New>
</Arg>
</Call>
</New>
</Arg>
</Call>

View File

@ -0,0 +1,159 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import static org.ops4j.pax.exam.CoreOptions.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import junit.framework.Assert;
import org.eclipse.jetty.client.ContentExchange;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Inject;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.container.def.PaxRunnerOptions;
import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
/**
* TestJettyOSGiBootContextAsService
*
* Tests deployment of a ContextHandler as an osgi Service.
*
* Tests the ServiceContextProvider.
*
*/
@RunWith( JUnit4TestRunner.class )
public class TestJettyOSGiBootContextAsService extends AbstractTestOSGi
{
private static final boolean LOGGING_ENABLED = false;
private static final boolean REMOTE_DEBUGGING = false;
@Inject
BundleContext bundleContext = null;
@Configuration
public static Option[] configure()
{
ArrayList<Option> options = new ArrayList<Option>();
addMoreOSGiContainers(options);
options.add(CoreOptions.junitBundles());
options.addAll(configurteJettyHomeAndPort("jetty-selector.xml"));
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*",
"org.w3c.*", "javax.xml.*"));
options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
//a bundle that registers a webapp as a service for the jetty osgi core to pick up and deploy
options.add(mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "test-jetty-osgi-webapp" ).versionAsInProject().start());
// Enable Logging
if(LOGGING_ENABLED) {
options.addAll(Arrays.asList(options(
// install log service using pax runners profile abstraction (there are more profiles, like DS)
// logProfile(),
// this is how you set the default log level when using pax logging (logProfile)
systemProperty( "org.ops4j.pax.logging.DefaultServiceLog.level" ).value( "INFO" )
)));
}
return options.toArray(new Option[options.size()]);
}
public static List<Option> configureJettyHomeAndPort(String jettySelectorFileName)
{
File etcFolder = new File("src/test/config/etc");
String etc = "file://" + etcFolder.getAbsolutePath();
List<Option> options = new ArrayList<Option>();
options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS)
.value(etc + "/jetty.xml;" +
etc + "/" + jettySelectorFileName + ";" +
etc + "/jetty-deployer.xml;" +
etc + "/jetty-testrealm.xml"));
options.add(systemProperty("jetty.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT)));
options.add(systemProperty("jetty.home").value(etcFolder.getParentFile().getAbsolutePath()));
return options;
}
@Test
public void assertAllBundlesActiveOrResolved()
{
assertAllBundlesActiveOrResolved(bundleContext);
}
/**
*/
@Test
public void testContextHandlerAsOSGiService() throws Exception
{
//now test the context
HttpClient client = new HttpClient();
try
{
client.start();
ContentResponse response = client.GET(""http://127.0.0.1:"+
TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT+"/acme/index.html");
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
String content = new String(response.getContent());
Assert.assertTrue(content.indexOf("<h1>Test OSGi Context</h1>") != -1);
}
finally
{
client.stop();
}
ServiceReference[] refs = bundleContext.getServiceReferences(ContextHandler.class.getName(), null);
Assert.assertNotNull(refs);
Assert.assertEquals(1,refs.length);
String[] keys = refs[0].getPropertyKeys();
if (keys != null)
{
for (String k:keys)
System.err.println("service property: "+k+", "+refs[0].getProperty(k));
}
ContextHandler ch = (ContextHandler)bundleContext.getService(refs[0]);
Assert.assertEquals("/acme", ch.getContextPath());
testWebBundle.stop();
//Check you can see CONTEXT DESTROYED on stderr. TODO: think of a better way to communicate this to the test
}
}

View File

@ -0,0 +1,174 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import static org.ops4j.pax.exam.CoreOptions.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import junit.framework.Assert;
import org.eclipse.jetty.client.ContentExchange;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Inject;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.container.def.PaxRunnerOptions;
import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
/**
* TestJettyOSGiBootWebAppAsService
*
* Tests deployment of a WebAppContext as an osgi Service.
*
* Tests the ServiceWebAppProvider.
*
* Pax-Exam to make sure the jetty-osgi-boot can be started along with the httpservice web-bundle.
* Then make sure we can deploy an OSGi service on the top of this.
*/
@RunWith( JUnit4TestRunner.class )
public class TestJettyOSGiBootWebAppAsService extends AbstractTestOSGi
{
private static final boolean LOGGING_ENABLED = false;
private static final boolean REMOTE_DEBUGGING = false;
@Inject
BundleContext bundleContext = null;
@Configuration
public static Option[] configure()
{
ArrayList<Option> options = new ArrayList<Option>();
addMoreOSGiContainers(options);
options.add(CoreOptions.junitBundles());
optinos.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*",
"org.w3c.*", "javax.xml.*"));
options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
// Enable Logging
if(LOGGING_ENABLED) {
options.addAll(Arrays.asList(options(
// install log service using pax runners profile abstraction (there are more profiles, like DS)
// logProfile(),
// this is how you set the default log level when using pax logging (logProfile)
systemProperty( "org.ops4j.pax.logging.DefaultServiceLog.level" ).value( "INFO" )
)));
}
options.addAll(jspDependencies());
return options.toArray(new Option[options.size()]);
}
public static List<Option> configureJettyHomeAndPort(String jettySelectorFileName)
{
File etcFolder = new File("src/test/config/etc");
String etc = "file://" + etcFolder.getAbsolutePath();
List<Option> options = new ArrayList<Option>();
options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS)
.value(etc + "/jetty.xml;" +
etc + "/" + jettySelectorFileName + ";" +
etc + "/jetty-deployer.xml;" +
etc + "/jetty-testrealm.xml"));
options.add(systemProperty("jetty.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT)));
options.add(systemProperty("jetty.home").value(etcFolder.getParentFile().getAbsolutePath()));
return options;
}
public static List<Option> jspDependencies() {
List<Option> res = new ArrayList<Option>();
/* orbit deps */
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet.jsp" ).versionAsInProject());
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet.jsp.jstl" ).versionAsInProject());
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.el" ).versionAsInProject());
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "com.sun.el" ).versionAsInProject());
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.apache.jasper.glassfish" ).versionAsInProject());
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.apache.taglibs.standard.glassfish" ).versionAsInProject());
res.add(mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.eclipse.jdt.core" ).versionAsInProject());
/* jetty-osgi deps */
res.add(mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot-jsp" ).versionAsInProject().noStart());
//a bundle that registers a webapp as a service for the jetty osgi core to pick up and deploy
res.add(mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "test-jetty-osgi-webapp" ).versionAsInProject().start());
return res;
}
@Test
public void assertAllBundlesActiveOrResolved()
{
assertAllBundlesActiveOrResolved(bundleContext);
}
/**
*/
@Test
public void testBundle() throws Exception
{
//now test the jsp/dump.jsp
HttpClient client = new HttpClient();
try
{
client.start();
ContentResponse response = client.GET("http://127.0.0.1:"+
TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT+"/acme/index.html");
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
String content = new String(response.getContent());
Assert.assertTrue(content.indexOf("<h1>Test OSGi WebApp</h1>") != -1);
}
}
finally
{
client.stop();
}
ServiceReference[] refs = bundleContext.getServiceReferences(ContextHandler.class.getName(), null);
Assert.assertNotNull(refs);
Assert.assertEquals(1,refs.length);
WebAppContext wac = (WebAppContext)bundleContext.getService(refs[0]);
Assert.assertEquals("/acme", wac.getContextPath());
}
}