Removing accidental add
This commit is contained in:
parent
cec5f5e65a
commit
f1daea663a
|
@ -1,206 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,197 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,291 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,734 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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.io.FilenameFilter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.deploy.App;
|
|
||||||
import org.eclipse.jetty.deploy.AppProvider;
|
|
||||||
import org.eclipse.jetty.deploy.DeploymentManager;
|
|
||||||
import org.eclipse.jetty.deploy.providers.ScanningAppProvider;
|
|
||||||
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
|
|
||||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
|
||||||
import org.eclipse.jetty.util.Scanner;
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
|
||||||
import org.eclipse.jetty.util.resource.Resource;
|
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
|
||||||
import org.osgi.framework.Bundle;
|
|
||||||
import org.osgi.framework.BundleContext;
|
|
||||||
import org.osgi.framework.BundleException;
|
|
||||||
import org.osgi.framework.Constants;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AppProvider for OSGi. Supports the configuration of ContextHandlers and
|
|
||||||
* WebApps. Extends the AbstractAppProvider to support the scanning of context
|
|
||||||
* files located outside of the bundles.
|
|
||||||
* <p>
|
|
||||||
* This provider must not be called outside of jetty.boot: it should always be
|
|
||||||
* called via the OSGi service listener.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* This provider supports the same set of parameters than the WebAppProvider as
|
|
||||||
* it supports the deployment of WebAppContexts. Except for the scanning of the
|
|
||||||
* webapps directory.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* When the parameter autoInstallOSGiBundles is set to true, OSGi bundles that
|
|
||||||
* are located in the monitored directory are installed and started after the
|
|
||||||
* framework as finished auto-starting all the other bundles. Warning: only use
|
|
||||||
* this for development.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|
||||||
{
|
|
||||||
private static final Logger LOG = Log.getLogger(OSGiAppProvider.class);
|
|
||||||
|
|
||||||
private boolean _extractWars = true;
|
|
||||||
|
|
||||||
private boolean _parentLoaderPriority = false;
|
|
||||||
|
|
||||||
private String _defaultsDescriptor;
|
|
||||||
|
|
||||||
private String _tldBundles;
|
|
||||||
|
|
||||||
private String[] _configurationClasses;
|
|
||||||
|
|
||||||
private boolean _autoInstallOSGiBundles = true;
|
|
||||||
|
|
||||||
// Keep track of the bundles that were installed and that are waiting for
|
|
||||||
// the
|
|
||||||
// framework to complete its initialization.
|
|
||||||
Set<Bundle> _pendingBundlesToStart = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When a context file corresponds to a deployed bundle and is changed we
|
|
||||||
* reload the corresponding bundle.
|
|
||||||
*/
|
|
||||||
private static class Filter implements FilenameFilter
|
|
||||||
{
|
|
||||||
OSGiAppProvider _enclosedInstance;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean accept(File dir, String name)
|
|
||||||
{
|
|
||||||
File file = new File(dir, name);
|
|
||||||
if (fileMightBeAnOSGiBundle(file)) { return true; }
|
|
||||||
if (!file.isDirectory())
|
|
||||||
{
|
|
||||||
String contextName = getDeployedAppName(name);
|
|
||||||
if (contextName != null)
|
|
||||||
{
|
|
||||||
App app = _enclosedInstance.getDeployedApps().get(contextName);
|
|
||||||
return app != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param contextFileName for example myContext.xml
|
|
||||||
* @return The context, for example: myContext; null if this was not a
|
|
||||||
* suitable contextFileName.
|
|
||||||
*/
|
|
||||||
private static String getDeployedAppName(String contextFileName)
|
|
||||||
{
|
|
||||||
String lowername = contextFileName.toLowerCase(Locale.ENGLISH);
|
|
||||||
if (lowername.endsWith(".xml"))
|
|
||||||
{
|
|
||||||
String contextName = contextFileName.substring(0, lowername.length() - ".xml".length());
|
|
||||||
return contextName;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reading the display name of a webapp is really not sufficient for
|
|
||||||
* indexing the various deployed ContextHandlers.
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private String getContextHandlerAppName(ContextHandler context)
|
|
||||||
{
|
|
||||||
String appName = context.getDisplayName();
|
|
||||||
if (appName == null || appName.length() == 0 || getDeployedApps().containsKey(appName))
|
|
||||||
{
|
|
||||||
if (context instanceof WebAppContext)
|
|
||||||
{
|
|
||||||
appName = ((WebAppContext) context).getContextPath();
|
|
||||||
if (getDeployedApps().containsKey(appName))
|
|
||||||
{
|
|
||||||
appName = "noDisplayName" + context.getClass().getSimpleName() + context.hashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
appName = "noDisplayName" + context.getClass().getSimpleName() + context.hashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return appName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default OSGiAppProvider constructed when none are defined in the
|
|
||||||
* jetty.xml configuration.
|
|
||||||
*/
|
|
||||||
public OSGiAppProvider()
|
|
||||||
{
|
|
||||||
super(new Filter());
|
|
||||||
((Filter) super._filenameFilter)._enclosedInstance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default OSGiAppProvider constructed when none are defined in the
|
|
||||||
* jetty.xml configuration.
|
|
||||||
*
|
|
||||||
* @param contextsDir
|
|
||||||
*/
|
|
||||||
public OSGiAppProvider(File contextsDir) throws IOException
|
|
||||||
{
|
|
||||||
this();
|
|
||||||
setMonitoredDirResource(Resource.newResource(contextsDir.toURI()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the ContextHandler that was created by WebappRegistractionHelper
|
|
||||||
*
|
|
||||||
* @see AppProvider
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public ContextHandler createContextHandler(App app) throws Exception
|
|
||||||
{
|
|
||||||
// return pre-created Context
|
|
||||||
ContextHandler wah = app.getContextHandler();
|
|
||||||
if (wah == null)
|
|
||||||
{
|
|
||||||
// for some reason it was not defined when the App was constructed.
|
|
||||||
// we don't support this situation at this point.
|
|
||||||
// once the WebAppRegistrationHelper is refactored, the code
|
|
||||||
// that creates the ContextHandler will actually be here.
|
|
||||||
throw new IllegalStateException("The App must be passed the " + "instance of the ContextHandler when it is constructed");
|
|
||||||
}
|
|
||||||
if (_configurationClasses != null && wah instanceof WebAppContext)
|
|
||||||
{
|
|
||||||
((WebAppContext) wah).setConfigurationClasses(_configurationClasses);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_defaultsDescriptor != null)
|
|
||||||
((WebAppContext) wah).setDefaultsDescriptor(_defaultsDescriptor);
|
|
||||||
return app.getContextHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see AppProvider
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setDeploymentManager(DeploymentManager deploymentManager)
|
|
||||||
{
|
|
||||||
super.setDeploymentManager(deploymentManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getOriginId(Bundle contributor, String pathInBundle)
|
|
||||||
{
|
|
||||||
return contributor.getSymbolicName() + "-" + contributor.getVersion().toString() + (pathInBundle.startsWith("/") ? pathInBundle : "/" + pathInBundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param context
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public void addContext(Bundle contributor, String pathInBundle, ContextHandler context) throws Exception
|
|
||||||
{
|
|
||||||
addContext(getOriginId(contributor, pathInBundle), context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param context
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public void addContext(String originId, ContextHandler context) throws Exception
|
|
||||||
{
|
|
||||||
// TODO apply configuration specific to this provider
|
|
||||||
if (context instanceof WebAppContext)
|
|
||||||
{
|
|
||||||
((WebAppContext) context).setExtractWAR(isExtract());
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrap context as an App
|
|
||||||
App app = new App(getDeploymentManager(), this, originId, context);
|
|
||||||
String appName = getContextHandlerAppName(context);
|
|
||||||
getDeployedApps().put(appName, app);
|
|
||||||
getDeploymentManager().addApp(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the scanner of the context files directory. If we find the
|
|
||||||
* corresponding deployed App we reload it by returning the App. Otherwise
|
|
||||||
* we return null and nothing happens: presumably the corresponding OSGi
|
|
||||||
* webapp is not ready yet.
|
|
||||||
*
|
|
||||||
* @return the corresponding already deployed App so that it will be
|
|
||||||
* reloaded. Otherwise returns null.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected App createApp(String filename)
|
|
||||||
{
|
|
||||||
// find the corresponding bundle and ContextHandler or WebAppContext
|
|
||||||
// and reload the corresponding App.
|
|
||||||
// see the 2 pass of the refactoring of the WebAppRegistrationHelper.
|
|
||||||
String name = getDeployedAppName(filename);
|
|
||||||
if (name != null) { return getDeployedApps().get(name); }
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeContext(ContextHandler context) throws Exception
|
|
||||||
{
|
|
||||||
String appName = getContextHandlerAppName(context);
|
|
||||||
App app = getDeployedApps().remove(context.getDisplayName());
|
|
||||||
if (app == null)
|
|
||||||
{
|
|
||||||
// try harder to undeploy this context handler.
|
|
||||||
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=330098
|
|
||||||
appName = null;
|
|
||||||
for (Entry<String, App> deployedApp : getDeployedApps().entrySet())
|
|
||||||
{
|
|
||||||
if (deployedApp.getValue().getContextHandler() == context)
|
|
||||||
{
|
|
||||||
app = deployedApp.getValue();
|
|
||||||
appName = deployedApp.getKey();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (appName != null)
|
|
||||||
{
|
|
||||||
getDeployedApps().remove(appName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (app != null)
|
|
||||||
{
|
|
||||||
getDeploymentManager().removeApp(app);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The context xml directory. In fact it is the directory watched by the
|
|
||||||
* scanner.
|
|
||||||
*/
|
|
||||||
public File getContextXmlDirAsFile()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Resource monitoredDir = getMonitoredDirResource();
|
|
||||||
if (monitoredDir == null) return null;
|
|
||||||
return monitoredDir.getFile();
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
LOG.warn(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* The context xml directory. In fact it is the directory watched by the
|
|
||||||
* scanner.
|
|
||||||
*/
|
|
||||||
public String getContextXmlDir()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Resource monitoredDir = getMonitoredDirResource();
|
|
||||||
if (monitoredDir == null) return null;
|
|
||||||
return monitoredDir.getFile().toURI().toString();
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
LOG.warn(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isExtract()
|
|
||||||
{
|
|
||||||
return _extractWars;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExtract(boolean extract)
|
|
||||||
{
|
|
||||||
_extractWars = extract;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true when this app provider locates osgi bundles and features in
|
|
||||||
* its monitored directory and installs them. By default true if
|
|
||||||
* there is a folder to monitor.
|
|
||||||
*/
|
|
||||||
public boolean isAutoInstallOSGiBundles()
|
|
||||||
{
|
|
||||||
return _autoInstallOSGiBundles;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <autoInstallOSGiBundles>true</autoInstallOSGiBundles>
|
|
||||||
*
|
|
||||||
* @param installingOSGiBundles
|
|
||||||
*/
|
|
||||||
public void setAutoInstallOSGiBundles(boolean installingOSGiBundles)
|
|
||||||
{
|
|
||||||
_autoInstallOSGiBundles = installingOSGiBundles;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* Set the directory in which to look for context XML files.
|
|
||||||
* <p>
|
|
||||||
* If a webapp call "foo/" or "foo.war" is discovered in the monitored
|
|
||||||
* directory, then the ContextXmlDir is examined to see if a foo.xml file
|
|
||||||
* exists. If it does, then this deployer will not deploy the webapp and the
|
|
||||||
* ContextProvider should be used to act on the foo.xml file.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* Also if this directory contains some osgi bundles, it will install them.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @see ContextProvider
|
|
||||||
* @param contextsDir
|
|
||||||
*/
|
|
||||||
public void setContextXmlDir(String contextsDir)
|
|
||||||
{
|
|
||||||
setMonitoredDirName(contextsDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overridden to install the OSGi bundles found in the monitored folder.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void doStart() throws Exception
|
|
||||||
{
|
|
||||||
if (isAutoInstallOSGiBundles())
|
|
||||||
{
|
|
||||||
if (getMonitoredDirResource() == null)
|
|
||||||
{
|
|
||||||
setAutoInstallOSGiBundles(false);
|
|
||||||
LOG.info("Disable autoInstallOSGiBundles as there is not contexts folder to monitor.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
File scandir = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
scandir = getMonitoredDirResource().getFile();
|
|
||||||
if (!scandir.exists() || !scandir.isDirectory())
|
|
||||||
{
|
|
||||||
setAutoInstallOSGiBundles(false);
|
|
||||||
LOG.warn("Disable autoInstallOSGiBundles as the contexts folder '" + scandir.getAbsolutePath() + " does not exist.");
|
|
||||||
scandir = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
setAutoInstallOSGiBundles(false);
|
|
||||||
LOG.warn("Disable autoInstallOSGiBundles as the contexts folder '" + getMonitoredDirResource().getURI() + " does not exist.");
|
|
||||||
scandir = null;
|
|
||||||
}
|
|
||||||
if (scandir != null)
|
|
||||||
{
|
|
||||||
for (File file : scandir.listFiles())
|
|
||||||
{
|
|
||||||
if (fileMightBeAnOSGiBundle(file))
|
|
||||||
{
|
|
||||||
installBundle(file, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.doStart();
|
|
||||||
if (isAutoInstallOSGiBundles())
|
|
||||||
{
|
|
||||||
Scanner.ScanCycleListener scanCycleListner = new AutoStartWhenFrameworkHasCompleted(this);
|
|
||||||
super.addScannerListener(scanCycleListner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When the file is a jar or a folder, we look if it looks like an OSGi
|
|
||||||
* bundle. In that case we install it and start it.
|
|
||||||
* <p>
|
|
||||||
* Really a simple trick to get going quickly with development.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void fileAdded(String filename) throws Exception
|
|
||||||
{
|
|
||||||
File file = new File(filename);
|
|
||||||
if (isAutoInstallOSGiBundles() && file.exists() && fileMightBeAnOSGiBundle(file))
|
|
||||||
{
|
|
||||||
installBundle(file, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
super.fileAdded(filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param file
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static boolean fileMightBeAnOSGiBundle(File file)
|
|
||||||
{
|
|
||||||
if (file.isDirectory())
|
|
||||||
{
|
|
||||||
if (new File(file, "META-INF/MANIFEST.MF").exists()) { return true; }
|
|
||||||
}
|
|
||||||
else if (file.getName().endsWith(".jar")) { return true; }
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void fileChanged(String filename) throws Exception
|
|
||||||
{
|
|
||||||
File file = new File(filename);
|
|
||||||
if (isAutoInstallOSGiBundles() && fileMightBeAnOSGiBundle(file))
|
|
||||||
{
|
|
||||||
updateBundle(file);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
super.fileChanged(filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void fileRemoved(String filename) throws Exception
|
|
||||||
{
|
|
||||||
File file = new File(filename);
|
|
||||||
if (isAutoInstallOSGiBundles() && fileMightBeAnOSGiBundle(file))
|
|
||||||
{
|
|
||||||
uninstallBundle(file);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
super.fileRemoved(filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a bundle according to its location. In the version 1.6 of
|
|
||||||
* org.osgi.framework, BundleContext.getBundle(String) is what we want.
|
|
||||||
* However to support older versions of OSGi. We use our own local reference
|
|
||||||
* mechanism.
|
|
||||||
*
|
|
||||||
* @param location
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected Bundle getBundle(BundleContext bc, String location)
|
|
||||||
{
|
|
||||||
// not available in older versions of OSGi:
|
|
||||||
// return bc.getBundle(location);
|
|
||||||
for (Bundle b : bc.getBundles())
|
|
||||||
{
|
|
||||||
if (b.getLocation().equals(location)) { return b; }
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized Bundle installBundle(File file, boolean start)
|
|
||||||
{
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
BundleContext bc = JettyBootstrapActivator.getBundleContext();
|
|
||||||
String location = file.toURI().toString();
|
|
||||||
Bundle b = getBundle(bc, location);
|
|
||||||
if (b == null)
|
|
||||||
{
|
|
||||||
b = bc.installBundle(location);
|
|
||||||
}
|
|
||||||
if (b == null)
|
|
||||||
{
|
|
||||||
// not sure we will ever be here,
|
|
||||||
// most likely a BundleException was thrown
|
|
||||||
LOG.warn("The file " + location + " is not an OSGi bundle.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (start && b.getHeaders().get(Constants.FRAGMENT_HOST) == null)
|
|
||||||
{
|
|
||||||
// not a fragment, try to start it. if the framework has finished
|
|
||||||
// auto-starting.
|
|
||||||
if (!PackageAdminServiceTracker.INSTANCE.frameworkHasCompletedAutostarts())
|
|
||||||
{
|
|
||||||
if (_pendingBundlesToStart == null)
|
|
||||||
{
|
|
||||||
_pendingBundlesToStart = new HashSet<Bundle>();
|
|
||||||
}
|
|
||||||
_pendingBundlesToStart.add(b);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
b.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
catch (BundleException e)
|
|
||||||
{
|
|
||||||
LOG.warn("Unable to " + (start ? "start" : "install") + " the bundle " + file.getAbsolutePath(), e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void uninstallBundle(File file)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Bundle b = getBundle(JettyBootstrapActivator.getBundleContext(), file.toURI().toString());
|
|
||||||
b.stop();
|
|
||||||
b.uninstall();
|
|
||||||
}
|
|
||||||
catch (BundleException e)
|
|
||||||
{
|
|
||||||
LOG.warn("Unable to uninstall the bundle " + file.getAbsolutePath(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateBundle(File file)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Bundle b = getBundle(JettyBootstrapActivator.getBundleContext(), file.toURI().toString());
|
|
||||||
if (b == null)
|
|
||||||
{
|
|
||||||
installBundle(file, true);
|
|
||||||
}
|
|
||||||
else if (b.getState() == Bundle.ACTIVE)
|
|
||||||
{
|
|
||||||
b.update();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
b.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (BundleException e)
|
|
||||||
{
|
|
||||||
LOG.warn("Unable to update the bundle " + file.getAbsolutePath(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* At the end of each scan, if there are some bundles to be started, look if the
|
|
||||||
* framework has completed its autostart. In that case start those bundles.
|
|
||||||
*/
|
|
||||||
class AutoStartWhenFrameworkHasCompleted implements Scanner.ScanCycleListener
|
|
||||||
{
|
|
||||||
private static final Logger LOG = Log.getLogger(AutoStartWhenFrameworkHasCompleted.class);
|
|
||||||
|
|
||||||
private final OSGiAppProvider _appProvider;
|
|
||||||
|
|
||||||
AutoStartWhenFrameworkHasCompleted(OSGiAppProvider appProvider)
|
|
||||||
{
|
|
||||||
_appProvider = appProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void scanStarted(int cycle) throws Exception
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void scanEnded(int cycle) throws Exception
|
|
||||||
{
|
|
||||||
if (_appProvider._pendingBundlesToStart != null && PackageAdminServiceTracker.INSTANCE.frameworkHasCompletedAutostarts())
|
|
||||||
{
|
|
||||||
Iterator<Bundle> it = _appProvider._pendingBundlesToStart.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Bundle b = it.next();
|
|
||||||
if (b.getHeaders().get(Constants.FRAGMENT_HOST) != null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
b.start();
|
|
||||||
}
|
|
||||||
catch (BundleException e)
|
|
||||||
{
|
|
||||||
LOG.warn("Unable to start the bundle " + b.getLocation(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
_appProvider._pendingBundlesToStart = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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";
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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;
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,338 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,172 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,444 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,390 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,213 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,285 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,909 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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.BufferedInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
|
|
||||||
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
|
|
||||||
import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelper;
|
|
||||||
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
|
|
||||||
import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
|
|
||||||
import org.eclipse.jetty.osgi.boot.utils.internal.DefaultBundleClassLoaderHelper;
|
|
||||||
import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
|
|
||||||
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
|
|
||||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
|
||||||
import org.eclipse.jetty.util.IO;
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
|
||||||
import org.eclipse.jetty.util.resource.Resource;
|
|
||||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
|
||||||
import org.eclipse.jetty.webapp.FragmentConfiguration;
|
|
||||||
import org.eclipse.jetty.webapp.TagLibConfiguration;
|
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
|
||||||
import org.eclipse.jetty.webapp.WebInfConfiguration;
|
|
||||||
import org.eclipse.jetty.xml.XmlConfiguration;
|
|
||||||
import org.osgi.framework.Bundle;
|
|
||||||
import org.osgi.framework.BundleContext;
|
|
||||||
import org.osgi.framework.BundleReference;
|
|
||||||
import org.osgi.service.packageadmin.PackageAdmin;
|
|
||||||
import org.osgi.util.tracker.ServiceTracker;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bridges the jetty deployers with the OSGi lifecycle where applications are
|
|
||||||
* managed inside OSGi-bundles.
|
|
||||||
* <p>
|
|
||||||
* This class should be called as a consequence of the activation of a new
|
|
||||||
* service that is a ContextHandler.<br/>
|
|
||||||
* This way the new webapps are exposed as OSGi services.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* Helper methods to register a bundle that is a web-application or a context.
|
|
||||||
* </p>
|
|
||||||
* Limitations:
|
|
||||||
* <ul>
|
|
||||||
* <li>support for jarred webapps is somewhat limited.</li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|
||||||
{
|
|
||||||
|
|
||||||
private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName());
|
|
||||||
|
|
||||||
private static boolean INITIALIZED = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
|
|
||||||
* equinox and apache-felix fragment bundles that are specific to an OSGi
|
|
||||||
* implementation should set a different implementation.
|
|
||||||
*/
|
|
||||||
public static BundleClassLoaderHelper BUNDLE_CLASS_LOADER_HELPER = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
|
|
||||||
* equinox and apache-felix fragment bundles that are specific to an OSGi
|
|
||||||
* implementation should set a different implementation.
|
|
||||||
*/
|
|
||||||
public static BundleFileLocatorHelper BUNDLE_FILE_LOCATOR_HELPER = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
|
|
||||||
* equinox and apache-felix fragment bundles that are specific to an OSGi
|
|
||||||
* implementation should set a different implementation.
|
|
||||||
* <p>
|
|
||||||
* Several of those objects can be added here: For example we could have an
|
|
||||||
* optional fragment that setups a specific implementation of JSF for the
|
|
||||||
* whole of jetty-osgi.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public static Collection<WebappRegistrationCustomizer> JSP_REGISTRATION_HELPERS = new ArrayList<WebappRegistrationCustomizer>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* this class loader loads the jars inside {$jetty.home}/lib/ext it is meant
|
|
||||||
* as a migration path and for jars that are not OSGi ready. also gives
|
|
||||||
* access to the jsp jars.
|
|
||||||
*/
|
|
||||||
// private URLClassLoader _libExtClassLoader;
|
|
||||||
|
|
||||||
private ServerInstanceWrapper _wrapper;
|
|
||||||
|
|
||||||
public WebBundleDeployerHelper(ServerInstanceWrapper wrapper)
|
|
||||||
{
|
|
||||||
staticInit();
|
|
||||||
_wrapper = wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inject the customizing classes that might be defined in fragment bundles.
|
|
||||||
public static synchronized void staticInit()
|
|
||||||
{
|
|
||||||
if (!INITIALIZED)
|
|
||||||
{
|
|
||||||
INITIALIZED = true;
|
|
||||||
// setup the custom BundleClassLoaderHelper
|
|
||||||
try
|
|
||||||
{
|
|
||||||
BUNDLE_CLASS_LOADER_HELPER = (BundleClassLoaderHelper) Class.forName(BundleClassLoaderHelper.CLASS_NAME).newInstance();
|
|
||||||
}
|
|
||||||
catch (Throwable t)
|
|
||||||
{
|
|
||||||
// System.err.println("support for equinox and felix");
|
|
||||||
BUNDLE_CLASS_LOADER_HELPER = new DefaultBundleClassLoaderHelper();
|
|
||||||
}
|
|
||||||
// setup the custom FileLocatorHelper
|
|
||||||
try
|
|
||||||
{
|
|
||||||
BUNDLE_FILE_LOCATOR_HELPER = (BundleFileLocatorHelper) Class.forName(BundleFileLocatorHelper.CLASS_NAME).newInstance();
|
|
||||||
}
|
|
||||||
catch (Throwable t)
|
|
||||||
{
|
|
||||||
// System.err.println("no jsp/jasper support");
|
|
||||||
BUNDLE_FILE_LOCATOR_HELPER = new DefaultFileLocatorHelper();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 WebAppContext registerWebapplication(Bundle bundle, String webappFolderPath, String contextPath, String extraClasspath,
|
|
||||||
String overrideBundleInstallLocation, String requireTldBundle, String webXmlPath, String defaultWebXmlPath,
|
|
||||||
WebAppContext webAppContext)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
File bundleInstall = overrideBundleInstallLocation == null ? BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(bundle) : new File(overrideBundleInstallLocation);
|
|
||||||
File webapp = null;
|
|
||||||
URL baseWebappInstallURL = null;
|
|
||||||
|
|
||||||
if (webappFolderPath != null && webappFolderPath.length() != 0 && !webappFolderPath.equals("."))
|
|
||||||
{
|
|
||||||
if (webappFolderPath.startsWith("/") || webappFolderPath.startsWith("file:"))
|
|
||||||
{
|
|
||||||
webapp = new File(webappFolderPath);
|
|
||||||
}
|
|
||||||
else if (bundleInstall != null && bundleInstall.isDirectory())
|
|
||||||
{
|
|
||||||
webapp = new File(bundleInstall, webappFolderPath);
|
|
||||||
}
|
|
||||||
else if (bundleInstall != null)
|
|
||||||
{
|
|
||||||
Enumeration<URL> urls = BUNDLE_FILE_LOCATOR_HELPER.findEntries(bundle, webappFolderPath);
|
|
||||||
if (urls != null && urls.hasMoreElements())
|
|
||||||
{
|
|
||||||
baseWebappInstallURL = urls.nextElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
webapp = bundleInstall;
|
|
||||||
}
|
|
||||||
if (baseWebappInstallURL == null && (webapp == null || !webapp.exists()))
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("Unable to locate " + webappFolderPath
|
|
||||||
+ " inside "
|
|
||||||
+ (bundleInstall != null ? bundleInstall.getAbsolutePath() : "unlocated bundle '" + bundle.getSymbolicName()+ "'"));
|
|
||||||
}
|
|
||||||
if (baseWebappInstallURL == null && webapp != null)
|
|
||||||
{
|
|
||||||
baseWebappInstallURL = webapp.toURI().toURL();
|
|
||||||
}
|
|
||||||
return registerWebapplication(bundle, webappFolderPath, baseWebappInstallURL, contextPath, extraClasspath, bundleInstall, requireTldBundle, webXmlPath,
|
|
||||||
defaultWebXmlPath, webAppContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see
|
|
||||||
* org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#
|
|
||||||
* registerWebapplication(org.osgi.framework.Bundle, java.lang.String,
|
|
||||||
* java.io.File, java.lang.String, java.lang.String, java.io.File,
|
|
||||||
* java.lang.String, java.lang.String)
|
|
||||||
*/
|
|
||||||
private WebAppContext registerWebapplication(Bundle contributor, String pathInBundleToWebApp, URL baseWebappInstallURL, String contextPath,
|
|
||||||
String extraClasspath, File bundleInstall, String requireTldBundle, String webXmlPath,
|
|
||||||
String defaultWebXmlPath, WebAppContext context)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
|
|
||||||
String[] oldServerClasses = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
// apply any META-INF/context.xml file that is found to configure
|
|
||||||
// the webapp first
|
|
||||||
applyMetaInfContextXml(contributor, context);
|
|
||||||
|
|
||||||
// make sure we provide access to all the jetty bundles by going
|
|
||||||
// through this bundle.
|
|
||||||
OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
|
|
||||||
// configure with access to all jetty classes and also all the
|
|
||||||
// classes
|
|
||||||
// that the contributor gives access to.
|
|
||||||
Thread.currentThread().setContextClassLoader(composite);
|
|
||||||
|
|
||||||
// converts bundleentry: protocol
|
|
||||||
baseWebappInstallURL = DefaultFileLocatorHelper.getLocalURL(baseWebappInstallURL);
|
|
||||||
|
|
||||||
context.setWar(baseWebappInstallURL.toString());
|
|
||||||
context.setContextPath(contextPath);
|
|
||||||
context.setExtraClasspath(extraClasspath);
|
|
||||||
|
|
||||||
if (webXmlPath != null && webXmlPath.length() != 0)
|
|
||||||
{
|
|
||||||
File webXml = null;
|
|
||||||
if (webXmlPath.startsWith("/") || webXmlPath.startsWith("file:/"))
|
|
||||||
{
|
|
||||||
webXml = new File(webXmlPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
webXml = new File(bundleInstall, webXmlPath);
|
|
||||||
}
|
|
||||||
if (webXml.exists())
|
|
||||||
{
|
|
||||||
context.setDescriptor(webXml.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultWebXmlPath == null || defaultWebXmlPath.length() == 0)
|
|
||||||
{
|
|
||||||
// use the one defined by the OSGiAppProvider.
|
|
||||||
defaultWebXmlPath = _wrapper.getOSGiAppProvider().getDefaultsDescriptor();
|
|
||||||
}
|
|
||||||
if (defaultWebXmlPath != null && defaultWebXmlPath.length() != 0)
|
|
||||||
{
|
|
||||||
File defaultWebXml = null;
|
|
||||||
if (defaultWebXmlPath.startsWith("/") || defaultWebXmlPath.startsWith("file:/"))
|
|
||||||
{
|
|
||||||
defaultWebXml = new File(defaultWebXmlPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
defaultWebXml = new File(bundleInstall, defaultWebXmlPath);
|
|
||||||
}
|
|
||||||
if (defaultWebXml.exists())
|
|
||||||
{
|
|
||||||
context.setDefaultsDescriptor(defaultWebXml.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// other parameters that might be defines on the OSGiAppProvider:
|
|
||||||
context.setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority());
|
|
||||||
|
|
||||||
configureWebappClassLoader(contributor, context, composite, requireTldBundle);
|
|
||||||
configureWebAppContext(context, contributor, requireTldBundle);
|
|
||||||
|
|
||||||
// @see
|
|
||||||
// org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext)
|
|
||||||
// during initialization of the webapp all the jetty packages are
|
|
||||||
// visible
|
|
||||||
// through the webapp classloader.
|
|
||||||
oldServerClasses = context.getServerClasses();
|
|
||||||
context.setServerClasses(null);
|
|
||||||
|
|
||||||
_wrapper.getOSGiAppProvider().addContext(contributor, pathInBundleToWebApp, context);
|
|
||||||
|
|
||||||
// support for patch resources. ideally this should be done inside a
|
|
||||||
// configurator.
|
|
||||||
List<Resource> patchResources = (List<Resource>) context.getAttribute(WebInfConfiguration.RESOURCE_URLS + ".patch");
|
|
||||||
if (patchResources != null)
|
|
||||||
{
|
|
||||||
LinkedList<Resource> resourcesPath = new LinkedList<Resource>();
|
|
||||||
// place the patch resources at the beginning of the lookup
|
|
||||||
// path.
|
|
||||||
resourcesPath.addAll(patchResources);
|
|
||||||
// then place the ones from the host web bundle.
|
|
||||||
Resource hostResources = context.getBaseResource();
|
|
||||||
if (hostResources instanceof ResourceCollection)
|
|
||||||
{
|
|
||||||
for (Resource re : ((ResourceCollection) hostResources).getResources())
|
|
||||||
{
|
|
||||||
resourcesPath.add(re);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
resourcesPath.add(hostResources);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceCollection rc = new ResourceCollection(resourcesPath.toArray(new Resource[resourcesPath.size()]));
|
|
||||||
context.setBaseResource(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (context != null && oldServerClasses != null)
|
|
||||||
{
|
|
||||||
context.setServerClasses(oldServerClasses);
|
|
||||||
}
|
|
||||||
Thread.currentThread().setContextClassLoader(contextCl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see
|
|
||||||
* org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#
|
|
||||||
* unregister(org.eclipse.jetty.server.handler.ContextHandler)
|
|
||||||
*/
|
|
||||||
public void unregister(ContextHandler contextHandler)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
_wrapper.getOSGiAppProvider().removeContext(contextHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see
|
|
||||||
* org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#
|
|
||||||
* registerContext(org.osgi.framework.Bundle, java.lang.String,
|
|
||||||
* java.lang.String, java.lang.String)
|
|
||||||
*/
|
|
||||||
public ContextHandler registerContext(Bundle contributor, String contextFileRelativePath, String extraClasspath, String overrideBundleInstallLocation,
|
|
||||||
String requireTldBundle, ContextHandler handler)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
File contextsHome = _wrapper.getOSGiAppProvider().getContextXmlDirAsFile();
|
|
||||||
if (contextsHome != null)
|
|
||||||
{
|
|
||||||
File prodContextFile = new File(contextsHome, contributor.getSymbolicName() + "/" + contextFileRelativePath);
|
|
||||||
if (prodContextFile.exists()) { return registerContext(contributor, contextFileRelativePath, prodContextFile, extraClasspath,
|
|
||||||
overrideBundleInstallLocation, requireTldBundle, handler); }
|
|
||||||
}
|
|
||||||
File rootFolder = overrideBundleInstallLocation != null ? Resource.newResource(overrideBundleInstallLocation).getFile() : BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(contributor);
|
|
||||||
File contextFile = rootFolder != null ? new File(rootFolder, contextFileRelativePath) : null;
|
|
||||||
if (contextFile != null && contextFile.exists())
|
|
||||||
{
|
|
||||||
return registerContext(contributor, contextFileRelativePath, contextFile, extraClasspath, overrideBundleInstallLocation, requireTldBundle, handler);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (contextFileRelativePath.startsWith("./"))
|
|
||||||
{
|
|
||||||
contextFileRelativePath = contextFileRelativePath.substring(1);
|
|
||||||
}
|
|
||||||
if (!contextFileRelativePath.startsWith("/"))
|
|
||||||
{
|
|
||||||
contextFileRelativePath = "/" + contextFileRelativePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
URL contextURL = contributor.getEntry(contextFileRelativePath);
|
|
||||||
if (contextURL != null)
|
|
||||||
{
|
|
||||||
Resource r = Resource.newResource(contextURL);
|
|
||||||
return registerContext(contributor, contextFileRelativePath, r.getInputStream(), extraClasspath, overrideBundleInstallLocation,
|
|
||||||
requireTldBundle, handler);
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Could not find the context " + "file "
|
|
||||||
+ contextFileRelativePath
|
|
||||||
+ " for the bundle "
|
|
||||||
+ contributor.getSymbolicName()
|
|
||||||
+ (overrideBundleInstallLocation != null ? " using the install location " + overrideBundleInstallLocation : ""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This type of registration relies on jetty's complete context xml file.
|
|
||||||
* Context encompasses jndi and all other things. This makes the definition
|
|
||||||
* of the webapp a lot more self-contained.
|
|
||||||
*
|
|
||||||
* @param webapp
|
|
||||||
* @param contextPath
|
|
||||||
* @param classInBundle
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private ContextHandler registerContext(Bundle contributor, String pathInBundle, File contextFile, String extraClasspath,
|
|
||||||
String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
InputStream contextFileInputStream = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
contextFileInputStream = new BufferedInputStream(new FileInputStream(contextFile));
|
|
||||||
return registerContext(contributor, pathInBundle, contextFileInputStream, extraClasspath, overrideBundleInstallLocation, requireTldBundle, handler);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
IO.close(contextFileInputStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param contributor
|
|
||||||
* @param contextFileInputStream
|
|
||||||
* @return The ContextHandler created and registered or null if it did not
|
|
||||||
* happen.
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private ContextHandler registerContext(Bundle contributor, String pathInsideBundle, InputStream contextFileInputStream, String extraClasspath,
|
|
||||||
String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
|
|
||||||
String[] oldServerClasses = null;
|
|
||||||
WebAppContext webAppContext = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// make sure we provide access to all the jetty bundles by going
|
|
||||||
// through this bundle.
|
|
||||||
OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
|
|
||||||
// configure with access to all jetty classes and also all the
|
|
||||||
// classes
|
|
||||||
// that the contributor gives access to.
|
|
||||||
Thread.currentThread().setContextClassLoader(composite);
|
|
||||||
ContextHandler context = createContextHandler(handler, contributor, contextFileInputStream, extraClasspath, overrideBundleInstallLocation,
|
|
||||||
requireTldBundle);
|
|
||||||
if (context == null) { return null;// did not happen
|
|
||||||
}
|
|
||||||
|
|
||||||
// ok now register this webapp. we checked when we started jetty
|
|
||||||
// that there
|
|
||||||
// was at least one such handler for webapps.
|
|
||||||
// the actual registration must happen via the new Deployment API.
|
|
||||||
// _ctxtHandler.addHandler(context);
|
|
||||||
|
|
||||||
configureWebappClassLoader(contributor, context, composite, requireTldBundle);
|
|
||||||
if (context instanceof WebAppContext)
|
|
||||||
{
|
|
||||||
webAppContext = (WebAppContext) context;
|
|
||||||
// @see
|
|
||||||
// org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext)
|
|
||||||
oldServerClasses = webAppContext.getServerClasses();
|
|
||||||
webAppContext.setServerClasses(null);
|
|
||||||
}
|
|
||||||
_wrapper.getOSGiAppProvider().addContext(contributor, pathInsideBundle, context);
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (webAppContext != null)
|
|
||||||
{
|
|
||||||
webAppContext.setServerClasses(oldServerClasses);
|
|
||||||
}
|
|
||||||
Thread.currentThread().setContextClassLoader(contextCl);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies the properties of WebAppDeployer as defined in jetty.xml.
|
|
||||||
*
|
|
||||||
* @see {WebAppDeployer#scan} around the comment
|
|
||||||
* <code>// configure it</code>
|
|
||||||
*/
|
|
||||||
protected void configureWebAppContext(ContextHandler wah, Bundle contributor, String requireTldBundle)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
// rfc66
|
|
||||||
wah.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, contributor.getBundleContext());
|
|
||||||
|
|
||||||
// spring-dm-1.2.1 looks for the BundleContext as a different attribute.
|
|
||||||
// not a spec... but if we want to support
|
|
||||||
// org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
|
|
||||||
// then we need to do this to:
|
|
||||||
wah.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), contributor.getBundleContext());
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
wah.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, contributor);
|
|
||||||
|
|
||||||
// pass the value of the require tld bundle so that the
|
|
||||||
// TagLibOSGiConfiguration
|
|
||||||
// can pick it up.
|
|
||||||
wah.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundle);
|
|
||||||
|
|
||||||
Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(contributor);
|
|
||||||
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.
|
|
||||||
TreeMap<String, Resource> patchResourcesPath = new TreeMap<String, Resource>();
|
|
||||||
TreeMap<String, Resource> appendedResourcesPath = new TreeMap<String, Resource>();
|
|
||||||
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 = DefaultFileLocatorHelper.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 "
|
|
||||||
+ " the fragment '"
|
|
||||||
+ frag.getSymbolicName()
|
|
||||||
+ "'"); }
|
|
||||||
patchFragUrl = DefaultFileLocatorHelper.getLocalURL(patchFragUrl);
|
|
||||||
String key = patchFragFolder.startsWith("/") ? patchFragFolder.substring(1) : patchFragFolder;
|
|
||||||
patchResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(patchFragUrl));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!appendedResourcesPath.isEmpty())
|
|
||||||
{
|
|
||||||
wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, new ArrayList<Resource>(appendedResourcesPath.values()));
|
|
||||||
}
|
|
||||||
if (!patchResourcesPath.isEmpty())
|
|
||||||
{
|
|
||||||
wah.setAttribute(WebInfConfiguration.RESOURCE_URLS + ".patch", new ArrayList<Resource>(patchResourcesPath.values()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wah instanceof WebAppContext)
|
|
||||||
{
|
|
||||||
// This is the equivalent of what MetaInfConfiguration does. For
|
|
||||||
// OSGi bundles without the JarScanner
|
|
||||||
WebAppContext webappCtxt = (WebAppContext) wah;
|
|
||||||
// take care of the web-fragments, meta-inf resources and tld
|
|
||||||
// resources:
|
|
||||||
// similar to what MetaInfConfiguration does.
|
|
||||||
List<Resource> frags = (List<Resource>) wah.getAttribute(FragmentConfiguration.FRAGMENT_RESOURCES);
|
|
||||||
List<Resource> resfrags = (List<Resource>) wah.getAttribute(WebInfConfiguration.RESOURCE_URLS);
|
|
||||||
List<Resource> tldfrags = (List<Resource>) wah.getAttribute(TagLibConfiguration.TLD_RESOURCES);
|
|
||||||
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
|
|
||||||
{
|
|
||||||
File fragFile = BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(frag);
|
|
||||||
// add it to the webinf jars collection:
|
|
||||||
// no need to check that it was not there yet: it
|
|
||||||
// was not there yet for sure.
|
|
||||||
Resource fragFileAsResource = Resource.newResource(fragFile.toURI());
|
|
||||||
webappCtxt.getMetaData().addWebInfJar(fragFileAsResource);
|
|
||||||
|
|
||||||
if (webFrag != null)
|
|
||||||
{
|
|
||||||
if (frags == null)
|
|
||||||
{
|
|
||||||
frags = new ArrayList<Resource>();
|
|
||||||
wah.setAttribute(FragmentConfiguration.FRAGMENT_RESOURCES, frags);
|
|
||||||
}
|
|
||||||
frags.add(fragFileAsResource);
|
|
||||||
}
|
|
||||||
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>();
|
|
||||||
wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, resfrags);
|
|
||||||
}
|
|
||||||
resfrags.add(Resource.newResource(DefaultFileLocatorHelper.getLocalURL(resourcesEntry)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tldEnum != null && tldEnum.hasMoreElements())
|
|
||||||
{
|
|
||||||
if (tldfrags == null)
|
|
||||||
{
|
|
||||||
tldfrags = new ArrayList<Resource>();
|
|
||||||
wah.setAttribute(TagLibConfiguration.TLD_RESOURCES, tldfrags);
|
|
||||||
}
|
|
||||||
while (tldEnum.hasMoreElements())
|
|
||||||
{
|
|
||||||
URL tldUrl = tldEnum.nextElement();
|
|
||||||
tldfrags.add(Resource.newResource(DefaultFileLocatorHelper.getLocalURL(tldUrl)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
__logger.warn("Unable to locate the bundle " + frag.getBundleId(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @See {@link ContextDeployer#scan}
|
|
||||||
* @param contextFile
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected ContextHandler createContextHandler(ContextHandler handlerToConfigure, Bundle bundle, File contextFile, String extraClasspath,
|
|
||||||
String overrideBundleInstallLocation, String requireTldBundle)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return createContextHandler(handlerToConfigure, bundle, new BufferedInputStream(new FileInputStream(contextFile)), extraClasspath,
|
|
||||||
overrideBundleInstallLocation, requireTldBundle);
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException e)
|
|
||||||
{
|
|
||||||
__logger.warn(e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @See {@link ContextDeployer#scan}
|
|
||||||
* @param contextFile
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected ContextHandler createContextHandler(ContextHandler handlerToConfigure, Bundle bundle, InputStream contextInputStream, String extraClasspath,
|
|
||||||
String overrideBundleInstallLocation, String requireTldBundle)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Do something identical to what the ContextProvider would have done:
|
|
||||||
* XmlConfiguration xmlConfiguration=new
|
|
||||||
* XmlConfiguration(resource.getURL()); HashMap properties = new
|
|
||||||
* HashMap(); properties.put("Server", _contexts.getServer()); if
|
|
||||||
* (_configMgr!=null) properties.putAll(_configMgr.getProperties());
|
|
||||||
*
|
|
||||||
* xmlConfiguration.setProperties(properties); ContextHandler
|
|
||||||
* context=(ContextHandler)xmlConfiguration.configure();
|
|
||||||
* context.setAttributes(new AttributesMap(_contextAttributes));
|
|
||||||
*/
|
|
||||||
try
|
|
||||||
{
|
|
||||||
XmlConfiguration xmlConfiguration = new XmlConfiguration(contextInputStream);
|
|
||||||
HashMap properties = new HashMap();
|
|
||||||
properties.put("Server", _wrapper.getServer());
|
|
||||||
|
|
||||||
// insert the bundle's location as a property.
|
|
||||||
setThisBundleHomeProperty(bundle, properties, overrideBundleInstallLocation);
|
|
||||||
xmlConfiguration.getProperties().putAll(properties);
|
|
||||||
|
|
||||||
ContextHandler context = null;
|
|
||||||
if (handlerToConfigure == null)
|
|
||||||
{
|
|
||||||
context = (ContextHandler) xmlConfiguration.configure();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xmlConfiguration.configure(handlerToConfigure);
|
|
||||||
context = handlerToConfigure;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context instanceof WebAppContext)
|
|
||||||
{
|
|
||||||
((WebAppContext) context).setExtraClasspath(extraClasspath);
|
|
||||||
((WebAppContext) context).setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority());
|
|
||||||
if (_wrapper.getOSGiAppProvider().getDefaultsDescriptor() != null && _wrapper.getOSGiAppProvider().getDefaultsDescriptor().length() != 0)
|
|
||||||
{
|
|
||||||
((WebAppContext) context).setDefaultsDescriptor(_wrapper.getOSGiAppProvider().getDefaultsDescriptor());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
configureWebAppContext(context, bundle, requireTldBundle);
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException e)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch (SAXException e)
|
|
||||||
{
|
|
||||||
__logger.warn(e);
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
__logger.warn(e);
|
|
||||||
}
|
|
||||||
catch (Throwable e)
|
|
||||||
{
|
|
||||||
__logger.warn(e);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
IO.close(contextInputStream);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configure a classloader onto the context. If the context is a
|
|
||||||
* WebAppContext, build a WebAppClassLoader that has access to all the jetty
|
|
||||||
* classes thanks to the classloader of the JettyBootStrapper bundle and
|
|
||||||
* also has access to the classloader of the bundle that defines this
|
|
||||||
* context.
|
|
||||||
* <p>
|
|
||||||
* If the context is not a WebAppContext, same but with a simpler
|
|
||||||
* URLClassLoader. Note that the URLClassLoader is pretty much fake: it
|
|
||||||
* delegate all actual classloading to the parent classloaders.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* The URL[] returned by the URLClassLoader create contained specifically
|
|
||||||
* the jars that some j2ee tools expect and look into. For example the jars
|
|
||||||
* that contain tld files for jasper's jstl support.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* Also as the jars in the lib folder and the classes in the classes folder
|
|
||||||
* might already be in the OSGi classloader we filter them out of the
|
|
||||||
* WebAppClassLoader
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @param contributor
|
|
||||||
* @param webapp
|
|
||||||
* @param contextPath
|
|
||||||
* @param classInBundle
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
protected void configureWebappClassLoader(Bundle contributor, ContextHandler context, OSGiWebappClassLoader webappClassLoader, String requireTldBundle)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
if (context instanceof WebAppContext)
|
|
||||||
{
|
|
||||||
WebAppContext webappCtxt = (WebAppContext) context;
|
|
||||||
context.setClassLoader(webappClassLoader);
|
|
||||||
webappClassLoader.setWebappContext(webappCtxt);
|
|
||||||
|
|
||||||
String pathsToRequiredBundles = getPathsToRequiredBundles(context, contributor, requireTldBundle);
|
|
||||||
if (pathsToRequiredBundles != null) webappClassLoader.addClassPath(pathsToRequiredBundles);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.setClassLoader(webappClassLoader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* No matter what the type of webapp, we create a WebappClassLoader.
|
|
||||||
*/
|
|
||||||
protected OSGiWebappClassLoader createWebappClassLoader(Bundle contributor)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
// we use a temporary WebAppContext object.
|
|
||||||
// if this is a real webapp we will set it on it a bit later: once we
|
|
||||||
// know.
|
|
||||||
OSGiWebappClassLoader webappClassLoader = new OSGiWebappClassLoader(_wrapper.getParentClassLoaderForWebapps(), new WebAppContext(), contributor,
|
|
||||||
BUNDLE_CLASS_LOADER_HELPER);
|
|
||||||
return webappClassLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void applyMetaInfContextXml(Bundle bundle, ContextHandler contextHandler)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
if (bundle == null) return;
|
|
||||||
if (contextHandler == null) return;
|
|
||||||
|
|
||||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
|
||||||
__logger.info("Context classloader = " + cl);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.currentThread().setContextClassLoader(_wrapper.getParentClassLoaderForWebapps());
|
|
||||||
|
|
||||||
// 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
|
|
||||||
__logger.info("Applying " + contextXmlUrl + " to " + contextHandler);
|
|
||||||
|
|
||||||
XmlConfiguration xmlConfiguration = new XmlConfiguration(contextXmlUrl);
|
|
||||||
HashMap properties = new HashMap();
|
|
||||||
properties.put("Server", _wrapper.getServer());
|
|
||||||
xmlConfiguration.getProperties().putAll(properties);
|
|
||||||
xmlConfiguration.configure(contextHandler);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Thread.currentThread().setContextClassLoader(cl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the property "this.bundle.install" to point to the location
|
|
||||||
* of the bundle. Useful when <SystemProperty name="this.bundle.home"/> is
|
|
||||||
* used.
|
|
||||||
*/
|
|
||||||
private void setThisBundleHomeProperty(Bundle bundle, HashMap<String, Object> properties, String overrideBundleInstallLocation)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
File location = overrideBundleInstallLocation != null ? new File(overrideBundleInstallLocation) : BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(bundle);
|
|
||||||
properties.put("this.bundle.install", location.getCanonicalPath());
|
|
||||||
properties.put("this.bundle.install.url", bundle.getEntry("/").toString());
|
|
||||||
}
|
|
||||||
catch (Throwable t)
|
|
||||||
{
|
|
||||||
__logger.warn("Unable to set 'this.bundle.install' " + " for the bundle " + bundle.getSymbolicName(), t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPathsToRequiredBundles(ContextHandler context, Bundle bundle, String requireTldBundle)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
if (requireTldBundle == null) return null;
|
|
||||||
|
|
||||||
StringBuilder paths = new StringBuilder();
|
|
||||||
PackageAdmin packAdmin = getBundleAdmin();
|
|
||||||
DefaultFileLocatorHelper fileLocatorHelper = new DefaultFileLocatorHelper();
|
|
||||||
|
|
||||||
String[] symbNames = requireTldBundle.split(", ");
|
|
||||||
|
|
||||||
for (String symbName : symbNames)
|
|
||||||
{
|
|
||||||
Bundle[] bs = packAdmin.getBundles(symbName, null);
|
|
||||||
if (bs == null || bs.length == 0) { throw new IllegalArgumentException("Unable to locate the bundle '" + symbName
|
|
||||||
+ "' specified in the "
|
|
||||||
+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE
|
|
||||||
+ " of the manifest of "
|
|
||||||
+ (bundle == null ? "unknown" : bundle.getSymbolicName())); }
|
|
||||||
|
|
||||||
File f = fileLocatorHelper.getBundleInstallLocation(bs[0]);
|
|
||||||
if (paths.length() > 0) paths.append(", ");
|
|
||||||
__logger.debug("getPathsToRequiredBundles: bundle path=" + bs[0].getLocation() + " uri=" + f.toURI());
|
|
||||||
paths.append(f.toURI().toURL().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return paths.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private PackageAdmin getBundleAdmin()
|
|
||||||
{
|
|
||||||
Bundle bootBundle = ((BundleReference) OSGiWebappConstants.class.getClassLoader()).getBundle();
|
|
||||||
ServiceTracker serviceTracker = new ServiceTracker(bootBundle.getBundleContext(), PackageAdmin.class.getName(), null);
|
|
||||||
serviceTracker.open();
|
|
||||||
|
|
||||||
return (PackageAdmin) serviceTracker.getService();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,271 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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.
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,93 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,242 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,446 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,383 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// 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 '\'';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue