Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Bartel2013-02-19 22:26:30 +0000
committerJan Bartel2013-02-19 22:26:30 +0000
commit9af433473961dab9f5d534031fe0a6daa615a2a6 (patch)
treedf568b77e825d0016af82c775bfed9cdc33b4aad /jetty-osgi/jetty-osgi-boot/src
parent39c5d6c141df91da0f480fbce2773d4f6749e347 (diff)
downloadorg.eclipse.jetty.project-9af433473961dab9f5d534031fe0a6daa615a2a6.tar.gz
org.eclipse.jetty.project-9af433473961dab9f5d534031fe0a6daa615a2a6.tar.xz
org.eclipse.jetty.project-9af433473961dab9f5d534031fe0a6daa615a2a6.zip
committing to save changes but does not yet compile
Diffstat (limited to 'jetty-osgi/jetty-osgi-boot/src')
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java206
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/annotations/AnnotationParser.java197
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java291
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/OSGiAppProvider.java (renamed from jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiAppProvider.java)0
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/OSGiServerConstants.java55
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java97
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java65
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloaderWithInsertedJettyClassloader.java69
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java338
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/IManagedJettyServerRegistry.java34
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java172
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java444
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/IWebBundleDeployerHelper.java87
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java390
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java213
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java285
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java (renamed from jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java)0
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java271
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelper.java54
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java93
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java60
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java242
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java446
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java383
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java4
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractContextProvider.java360
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractOSGiApp.java120
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java552
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java169
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleProvider.java28
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java238
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java16
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiDeployer.java61
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiMetaInfConfiguration.java116
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiServerConstants.java32
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiUndeployer.java44
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java273
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java66
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceContextProvider.java186
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceProvider.java29
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceWebAppProvider.java248
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java99
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java211
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/BundleFileLocatorHelperFactory.java58
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/IWebBundleDeployerHelper.java1
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java381
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java5
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java229
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelperFactory.java61
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java26
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/EventSender.java91
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/OSGiClassLoader.java221
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java5
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java51
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java99
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnectorListener.java268
-rw-r--r--jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnectorServletDelegate.java51
57 files changed, 8199 insertions, 692 deletions
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
new file mode 100644
index 0000000000..98ac22b6eb
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
@@ -0,0 +1,206 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.annotations;
+
+import org.eclipse.jetty.annotations.AbstractDiscoverableAnnotationHandler;
+import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
+import org.eclipse.jetty.annotations.ClassNameResolver;
+import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
+import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+/**
+ * Extend the AnnotationConfiguration to support OSGi:
+ * Look for annotations inside WEB-INF/lib and also in the fragments and required bundles.
+ * Discover them using a scanner adapted to OSGi instead of the jarscanner.
+ */
+public class AnnotationConfiguration extends org.eclipse.jetty.annotations.AnnotationConfiguration
+{
+
+ /**
+ * This parser scans the bundles using the OSGi APIs instead of assuming a jar.
+ */
+ @Override
+ protected org.eclipse.jetty.annotations.AnnotationParser createAnnotationParser()
+ {
+ return new AnnotationParser();
+ }
+
+ /**
+ * Here is the order in which jars and osgi artifacts are scanned for discoverable annotations.
+ * <ol>
+ * <li>The container jars are scanned.</li>
+ * <li>The WEB-INF/classes are scanned</li>
+ * <li>The osgi fragment to the web bundle are parsed.</li>
+ * <li>The WEB-INF/lib are scanned</li>
+ * <li>The required bundles are parsed</li>
+ * </ol>
+ */
+ @Override
+ public void parseWebInfLib (WebAppContext context, org.eclipse.jetty.annotations.AnnotationParser parser)
+ throws Exception
+ {
+ AnnotationParser oparser = (AnnotationParser)parser;
+
+ Bundle webbundle = (Bundle) context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
+ Bundle[] fragAndRequiredBundles = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(webbundle);
+ if (fragAndRequiredBundles != null)
+ {
+ //index:
+ for (Bundle bundle : fragAndRequiredBundles)
+ {
+ Resource bundleRes = oparser.indexBundle(bundle);
+ if (!context.getMetaData().getWebInfJars().contains(bundleRes))
+ {
+ context.getMetaData().addWebInfJar(bundleRes);
+ }
+ }
+
+ //scan the fragments
+ for (Bundle fragmentBundle : fragAndRequiredBundles)
+ {
+ if (fragmentBundle.getHeaders().get(Constants.FRAGMENT_HOST) != null)
+ {
+ //a fragment indeed:
+ parseFragmentBundle(context,oparser,webbundle,fragmentBundle);
+ }
+ }
+ }
+ //scan ourselves
+ parseWebBundle(context,oparser,webbundle);
+
+ //scan the WEB-INF/lib
+ super.parseWebInfLib(context,parser);
+ if (fragAndRequiredBundles != null)
+ {
+ //scan the required bundles
+ for (Bundle requiredBundle : fragAndRequiredBundles)
+ {
+ if (requiredBundle.getHeaders().get(Constants.FRAGMENT_HOST) == null)
+ {
+ //a bundle indeed:
+ parseRequiredBundle(context,oparser,webbundle,requiredBundle);
+ }
+ }
+ }
+ }
+
+ /**
+ * Scan a fragment bundle for servlet annotations
+ * @param context The webapp context
+ * @param parser The parser
+ * @param webbundle The current webbundle
+ * @param fragmentBundle The OSGi fragment bundle to scan
+ * @throws Exception
+ */
+ protected void parseFragmentBundle(WebAppContext context, AnnotationParser parser,
+ Bundle webbundle, Bundle fragmentBundle) throws Exception
+ {
+ parseBundle(context,parser,webbundle,fragmentBundle);
+ }
+
+ /**
+ * Scan a bundle required by the webbundle for servlet annotations
+ * @param context The webapp context
+ * @param parser The parser
+ * @param webbundle The current webbundle
+ * @param fragmentBundle The OSGi required bundle to scan
+ * @throws Exception
+ */
+ protected void parseWebBundle(WebAppContext context, AnnotationParser parser, Bundle webbundle)
+ throws Exception
+ {
+ parseBundle(context,parser,webbundle,webbundle);
+ }
+
+ /**
+ * Scan a bundle required by the webbundle for servlet annotations
+ * @param context The webapp context
+ * @param parser The parser
+ * @param webbundle The current webbundle
+ * @param fragmentBundle The OSGi required bundle to scan
+ * @throws Exception
+ */
+ protected void parseRequiredBundle(WebAppContext context, AnnotationParser parser,
+ Bundle webbundle, Bundle requiredBundle) throws Exception
+ {
+ parseBundle(context,parser,webbundle,requiredBundle);
+ }
+
+ protected void parseBundle(WebAppContext context, AnnotationParser parser,
+ Bundle webbundle, Bundle bundle) throws Exception
+ {
+
+ Resource bundleRes = parser.getResource(bundle);
+
+ parser.clearHandlers();
+ for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
+ {
+ if (h instanceof AbstractDiscoverableAnnotationHandler)
+ {
+ if (webbundle == bundle)
+ ((AbstractDiscoverableAnnotationHandler)h).setResource(null);
+ else
+ ((AbstractDiscoverableAnnotationHandler)h).setResource(bundleRes);
+ }
+ }
+ parser.registerHandlers(_discoverableAnnotationHandlers);
+ parser.registerHandler(_classInheritanceHandler);
+ parser.registerHandlers(_containerInitializerAnnotationHandlers);
+
+ parser.parse(bundle,createClassNameResolver(context));
+ }
+
+ /**
+ * Returns the same classname resolver than for the webInfjar scanner
+ * @param context
+ * @return
+ */
+ protected ClassNameResolver createClassNameResolver(final WebAppContext context)
+ {
+ return createClassNameResolver(context,true,false,false,false);
+ }
+
+ protected ClassNameResolver createClassNameResolver(final WebAppContext context,
+ final boolean excludeSysClass, final boolean excludeServerClass, final boolean excludeEverythingElse,
+ final boolean overrideIsParenLoaderIsPriority)
+ {
+ return new ClassNameResolver ()
+ {
+ public boolean isExcluded (String name)
+ {
+ if (context.isSystemClass(name)) return excludeSysClass;
+ if (context.isServerClass(name)) return excludeServerClass;
+ return excludeEverythingElse;
+ }
+
+ public boolean shouldOverride (String name)
+ {
+ //looking at system classpath
+ if (context.isParentLoaderPriority())
+ return overrideIsParenLoaderIsPriority;
+ return !overrideIsParenLoaderIsPriority;
+ }
+ };
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/annotations/AnnotationParser.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/annotations/AnnotationParser.java
new file mode 100644
index 0000000000..096cb8a821
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/annotations/AnnotationParser.java
@@ -0,0 +1,197 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.annotations;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URL;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import org.eclipse.jetty.annotations.ClassNameResolver;
+import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
+import org.eclipse.jetty.util.resource.Resource;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+/**
+ *
+ */
+public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationParser
+{
+ private Set<URI> _alreadyParsed = new HashSet<URI>();
+
+ private Map<URI,Bundle> _uriToBundle = new HashMap<URI, Bundle>();
+ private Map<Bundle,Resource> _resourceToBundle = new HashMap<Bundle,Resource>();
+ private Map<Bundle,URI> _bundleToUri = new HashMap<Bundle, URI>();
+
+ /**
+ * Keep track of a jetty URI Resource and its associated OSGi bundle.
+ * @param uri
+ * @param bundle
+ * @throws Exception
+ */
+ protected Resource indexBundle(Bundle bundle) throws Exception
+ {
+ File bundleFile = BundleFileLocatorHelper.DEFAULT.getBundleInstallLocation(bundle);
+ Resource resource = Resource.newResource(bundleFile.toURI());
+ URI uri = resource.getURI();
+ _uriToBundle.put(uri,bundle);
+ _bundleToUri.put(bundle,uri);
+ _resourceToBundle.put(bundle,resource);
+ return resource;
+ }
+ protected URI getURI(Bundle bundle)
+ {
+ return _bundleToUri.get(bundle);
+ }
+ protected Resource getResource(Bundle bundle)
+ {
+ return _resourceToBundle.get(bundle);
+ }
+ /**
+ *
+ */
+ @Override
+ public void parse (URI[] uris, ClassNameResolver resolver)
+ throws Exception
+ {
+ for (URI uri : uris)
+ {
+ Bundle associatedBundle = _uriToBundle.get(uri);
+ if (associatedBundle == null)
+ {
+ if (!_alreadyParsed.add(uri))
+ {
+ continue;
+ }
+ //a jar in WEB-INF/lib or the WEB-INF/classes
+ //use the behavior of the super class for a standard jar.
+ super.parse(new URI[] {uri},resolver);
+ }
+ else
+ {
+ parse(associatedBundle,resolver);
+ }
+ }
+ }
+
+ protected void parse(Bundle bundle, ClassNameResolver resolver)
+ throws Exception
+ {
+ URI uri = _bundleToUri.get(bundle);
+ if (!_alreadyParsed.add(uri))
+ {
+ return;
+ }
+
+ String bundleClasspath = (String)bundle.getHeaders().get(Constants.BUNDLE_CLASSPATH);
+ if (bundleClasspath == null)
+ {
+ bundleClasspath = ".";
+ }
+ //order the paths first by the number of tokens in the path second alphabetically.
+ TreeSet<String> paths = new TreeSet<String>(
+ new Comparator<String>()
+ {
+ public int compare(String o1, String o2)
+ {
+ int paths1 = new StringTokenizer(o1,"/",false).countTokens();
+ int paths2 = new StringTokenizer(o2,"/",false).countTokens();
+ if (paths1 == paths2)
+ {
+ return o1.compareTo(o2);
+ }
+ return paths2 - paths1;
+ }
+ });
+ boolean hasDotPath = false;
+ StringTokenizer tokenizer = new StringTokenizer(bundleClasspath, ",;", false);
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken().trim();
+ if (!token.startsWith("/"))
+ {
+ token = "/" + token;
+ }
+ if (token.equals("/."))
+ {
+ hasDotPath = true;
+ }
+ else if (!token.endsWith(".jar") && !token.endsWith("/"))
+ {
+ paths.add(token+"/");
+ }
+ else
+ {
+ paths.add(token);
+ }
+ }
+ //support the development environment: maybe the classes are inside bin or target/classes
+ //this is certainly not useful in production.
+ //however it makes our life so much easier during development.
+ if (bundle.getEntry("/.classpath") != null)
+ {
+ if (bundle.getEntry("/bin/") != null)
+ {
+ paths.add("/bin/");
+ }
+ else if (bundle.getEntry("/target/classes/") != null)
+ {
+ paths.add("/target/classes/");
+ }
+ }
+ Enumeration classes = bundle.findEntries("/","*.class",true);
+ if (classes == null)
+ {
+ return;
+ }
+ while (classes.hasMoreElements())
+ {
+ URL classUrl = (URL) classes.nextElement();
+ String path = classUrl.getPath();
+ //remove the longest path possible:
+ String name = null;
+ for (String prefixPath : paths)
+ {
+ if (path.startsWith(prefixPath))
+ {
+ name = path.substring(prefixPath.length());
+ break;
+ }
+ }
+ if (name == null && hasDotPath)
+ {
+ //remove the starting '/'
+ name = path.substring(1);
+ }
+ //transform into a classname to pass to the resolver
+ String shortName = name.replace('/', '.').substring(0,name.length()-6);
+ if ((resolver == null)|| (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
+ scanClass(classUrl.openStream());
+ }
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
new file mode 100644
index 0000000000..777ebdf608
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
@@ -0,0 +1,291 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.JettyServerServiceTracker;
+import org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper;
+import org.eclipse.jetty.osgi.boot.internal.webapp.JettyContextHandlerServiceTracker;
+import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleTrackerCustomizer;
+import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.BundleTracker;
+
+/**
+ * Bootstrap jetty and publish a default Server instance as an OSGi service.
+ *
+ * Listen for other Server instances to be published as services and support them as deployment targets.
+ *
+ * Listen for Bundles to be activated, and deploy those that represent webapps to one of the known Server instances.
+ *
+ * <ol>
+ * <li>basic servlet [ok]</li>
+ * <li>basic jetty.xml [ok]</li>
+ * <li>basic jetty.xml and jetty-plus.xml [ok]</li>
+ * <li>basic jsp [ok]</li>
+ * <li>jsp with tag-libs [ok]</li>
+ * <li>test-jndi with atomikos and derby inside ${jetty.home}/lib/ext [ok]</li>
+ * </ul>
+ */
+public class JettyBootstrapActivator implements BundleActivator
+{
+
+ private static JettyBootstrapActivator INSTANCE = null;
+
+ public static JettyBootstrapActivator getInstance()
+ {
+ return INSTANCE;
+ }
+
+ private ServiceRegistration _registeredServer;
+
+ private Server _server;
+
+ private JettyContextHandlerServiceTracker _jettyContextHandlerTracker;
+
+ private PackageAdminServiceTracker _packageAdminServiceTracker;
+
+ private BundleTracker _webBundleTracker;
+
+ private BundleContext _bundleContext;
+
+ private JettyServerServiceTracker _jettyServerServiceTracker;
+
+ /**
+ * Setup a new jetty Server, registers it as a service. Setup the Service
+ * tracker for the jetty ContextHandlers that are in charge of deploying the
+ * webapps. Setup the BundleListener that supports the extender pattern for
+ * the jetty ContextHandler.
+ *
+ * @param context
+ */
+ public void start(BundleContext context) throws Exception
+ {
+ INSTANCE = this;
+ _bundleContext = context;
+
+ // track other bundles and fragments attached to this bundle that we
+ // should activate.
+ _packageAdminServiceTracker = new PackageAdminServiceTracker(context);
+
+ // track Server instances that we should support as deployment targets
+ _jettyServerServiceTracker = new JettyServerServiceTracker();
+ context.addServiceListener(_jettyServerServiceTracker, "(objectclass=" + Server.class.getName() + ")");
+
+ // track ContextHandler class instances and deploy them to one of the known Servers
+ _jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(_jettyServerServiceTracker);
+ context.addServiceListener(_jettyContextHandlerTracker, "(objectclass=" + ContextHandler.class.getName() + ")");
+
+ // Create a default jetty instance right now.
+ DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context);
+
+ // track Bundles and deploy those that represent webapps to one of the known Servers
+ _webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer());
+ _webBundleTracker.open();
+ }
+
+ /**
+ * Stop the activator.
+ *
+ * @see
+ * org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception
+ {
+ try
+ {
+
+ if (_webBundleTracker != null)
+ {
+ _webBundleTracker.close();
+ _webBundleTracker = null;
+ }
+ if (_jettyContextHandlerTracker != null)
+ {
+ _jettyContextHandlerTracker.stop();
+ context.removeServiceListener(_jettyContextHandlerTracker);
+ _jettyContextHandlerTracker = null;
+ }
+ if (_jettyServerServiceTracker != null)
+ {
+ _jettyServerServiceTracker.stop();
+ context.removeServiceListener(_jettyServerServiceTracker);
+ _jettyServerServiceTracker = null;
+ }
+ if (_packageAdminServiceTracker != null)
+ {
+ _packageAdminServiceTracker.stop();
+ context.removeServiceListener(_packageAdminServiceTracker);
+ _packageAdminServiceTracker = null;
+ }
+ if (_registeredServer != null)
+ {
+ try
+ {
+ _registeredServer.unregister();
+ }
+ catch (IllegalArgumentException ill)
+ {
+ // already unregistered.
+ }
+ finally
+ {
+ _registeredServer = null;
+ }
+ }
+ }
+ finally
+ {
+ if (_server != null)
+ {
+ _server.stop();
+ }
+ INSTANCE = null;
+ }
+ }
+
+ /**
+ * Helper method that creates a new org.jetty.webapp.WebAppContext and
+ * registers it as an OSGi service. The tracker
+ * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
+ *
+ * @param contributor The bundle
+ * @param webappFolderPath The path to the root of the webapp. Must be a
+ * path relative to bundle; either an absolute path.
+ * @param contextPath The context path. Must start with "/"
+ * @throws Exception
+ */
+ public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath) throws Exception
+ {
+ checkBundleActivated();
+ WebAppContext contextHandler = new WebAppContext();
+ Dictionary<String,String> dic = new Hashtable<String,String>();
+ dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
+ dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
+ String requireTldBundle = (String) contributor.getHeaders().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
+ if (requireTldBundle != null)
+ {
+ dic.put(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE, requireTldBundle);
+ }
+ contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
+ }
+
+ /**
+ * Helper method that creates a new org.jetty.webapp.WebAppContext and
+ * registers it as an OSGi service. The tracker
+ * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
+ *
+ * @param contributor The bundle
+ * @param webappFolderPath The path to the root of the webapp. Must be a
+ * path relative to bundle; either an absolute path.
+ * @param contextPath The context path. Must start with "/"
+ * @param dic TODO: parameter description
+ * @throws Exception
+ */
+ public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath, Dictionary<String, String> dic) throws Exception
+ {
+ checkBundleActivated();
+ WebAppContext contextHandler = new WebAppContext();
+ dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
+ dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
+ contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
+ }
+
+ /**
+ * Helper method that creates a new skeleton of a ContextHandler and
+ * registers it as an OSGi service. The tracker
+ * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
+ *
+ * @param contributor The bundle that registers a new context
+ * @param contextFilePath The path to the file inside the bundle that
+ * defines the context.
+ * @throws Exception
+ */
+ public static void registerContext(Bundle contributor, String contextFilePath) throws Exception
+ {
+ registerContext(contributor, contextFilePath, new Hashtable<String, String>());
+ }
+
+ /**
+ * Helper method that creates a new skeleton of a ContextHandler and
+ * registers it as an OSGi service. The tracker
+ * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
+ *
+ * @param contributor The bundle that registers a new context
+ * @param contextFilePath The path to the file inside the bundle that
+ * defines the context.
+ * @param dic TODO: parameter description
+ * @throws Exception
+ */
+ public static void registerContext(Bundle contributor, String contextFilePath, Dictionary<String, String> dic) throws Exception
+ {
+ checkBundleActivated();
+ ContextHandler contextHandler = new ContextHandler();
+ dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH, contextFilePath);
+ dic.put(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE, Boolean.TRUE.toString());
+ contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
+ }
+
+ public static void unregister(String contextPath)
+ {
+ // todo
+ }
+
+ /**
+ * Since org.eclipse.jetty.osgi.boot does not have a lazy activation policy
+ * when one of the static methods to register a webapp is called we should
+ * make sure that the bundle is started.
+ */
+ private static void checkBundleActivated()
+ {
+ if (INSTANCE == null)
+ {
+ Bundle thisBundle = FrameworkUtil.getBundle(JettyBootstrapActivator.class);
+ try
+ {
+ thisBundle.start();
+ }
+ catch (BundleException e)
+ {
+ // nevermind.
+ }
+ }
+ }
+
+ /**
+ * @return The bundle context for this bundle.
+ */
+ public static BundleContext getBundleContext()
+ {
+ checkBundleActivated();
+ return INSTANCE._bundleContext;
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiAppProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/OSGiAppProvider.java
index b23e3fa796..b23e3fa796 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiAppProvider.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/OSGiAppProvider.java
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/OSGiServerConstants.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/OSGiServerConstants.java
new file mode 100644
index 0000000000..53b35c7fd6
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/OSGiServerConstants.java
@@ -0,0 +1,55 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+/**
+ * Name of the properties that configure a jetty Server OSGi service.
+ */
+public class OSGiServerConstants
+{
+ //for managed jetty instances, name of the configuration parameters
+ /**
+ * PID of the jetty servers's ManagedFactory
+ */
+ public static final String MANAGED_JETTY_SERVER_FACTORY_PID = "org.eclipse.jetty.osgi.boot.managedserverfactory";
+
+ /**
+ * The associated value of that configuration parameter is the name under which this
+ * instance of the jetty server is tracked.
+ * When a ContextHandler is deployed and it specifies the managedServerName property, it is deployed
+ * on the corresponding jetty managed server or it throws an exception: jetty server not available.
+ */
+ public static final String MANAGED_JETTY_SERVER_NAME = "managedServerName";
+ /**
+ * Name of the 'default' jetty server instance.
+ * Usually the first one to be created.
+ */
+ public static final String MANAGED_JETTY_SERVER_DEFAULT_NAME = "defaultJettyServer";
+
+ /**
+ * List of URLs to the jetty.xml files that configure th server.
+ */
+ public static final String MANAGED_JETTY_XML_CONFIG_URLS = "jetty.etc.config.urls";
+
+ /**
+ * List of URLs to the folders where the legacy J2EE shared libraries are stored aka lib/ext, lib/jsp etc.
+ */
+ public static final String MANAGED_JETTY_SHARED_LIB_FOLDER_URLS = "managedJettySharedLibFolderUrls";
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java
new file mode 100644
index 0000000000..3cb21232ed
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java
@@ -0,0 +1,97 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+/**
+ * Name of the service properties for a ContextHandler that configure a webapp deployed on jetty OSGi.
+ */
+public class OSGiWebappConstants
+{
+ /** url scheme to deploy war file as bundled webapp */
+ public static final String RFC66_WAR_URL_SCHEME = "war";
+
+ /**
+ * Name of the header that defines the context path for the embedded webapp.
+ */
+ public static final String RFC66_WEB_CONTEXTPATH = "Web-ContextPath";
+
+ /**
+ * Name of the header that defines the path to the folder where the jsp
+ * files are extracted.
+ */
+ public static final String RFC66_JSP_EXTRACT_LOCATION = "Jsp-ExtractLocation";
+
+ /** Name of the servlet context attribute that points to the bundle context. */
+ public static final String RFC66_OSGI_BUNDLE_CONTEXT = "osgi-bundlecontext";
+
+ /** Name of the servlet context attribute that points to the bundle object.
+ * We can't always rely on the bundle-context as there might be no such thing. */
+ public static final String JETTY_OSGI_BUNDLE = "osgi-bundle";
+
+ /** List of relative pathes within the bundle to the jetty context files. */
+ public static final String JETTY_CONTEXT_FILE_PATH = "Jetty-ContextFilePath";
+
+ /** path within the bundle to the folder that contains the basic resources. */
+ public static final String JETTY_WAR_FOLDER_PATH = "Jetty-WarFolderPath";
+
+ /** path within a fragment hosted by a web-bundle to a folder that contains basic resources.
+ * the path is appended to the lookup path where jetty locates static resources */
+ public static final String JETTY_WAR_FRAGMENT_FOLDER_PATH = "Jetty-WarFragmentFolderPath";
+
+ /** path within a fragment hosted by a web-bundle to a folder that contains basic resources.
+ * The path is prefixed to the lookup path where jetty locates static resources:
+ * this will override static resources with the same name in the web-bundle. */
+ public static final String JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH = "Jetty-WarPatchFragmentFolderPath";
+
+ // OSGi ContextHandler service properties.
+ /** web app context path */
+ public static final String SERVICE_PROP_CONTEXT_PATH = "contextPath";
+
+ /** Path to the web application base folder */
+ public static final String SERVICE_PROP_WAR = "war";
+
+ /** Extra classpath */
+ public static final String SERVICE_PROP_EXTRA_CLASSPATH = "extraClasspath";
+
+ /** jetty context file path */
+ public static final String SERVICE_PROP_CONTEXT_FILE_PATH = "contextFilePath";
+
+ /** web.xml file path */
+ public static final String SERVICE_PROP_WEB_XML_PATH = "webXmlFilePath";
+
+ /** defaultweb.xml file path */
+ public static final String SERVICE_PROP_DEFAULT_WEB_XML_PATH = "defaultWebXmlFilePath";
+
+ /**
+ * path to the base folder that overrides the computed bundle installation
+ * location if not null useful to install webapps or jetty context files
+ * that are in fact not embedded in a bundle
+ */
+ public static final String SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE = "thisBundleInstall";
+
+ /**
+ * Comma separated list of bundles that contain tld file used by the webapp.
+ */
+ public static final String REQUIRE_TLD_BUNDLE = "Require-TldBundle";
+ /**
+ * Comma separated list of bundles that contain tld file used by the webapp.
+ * Both the name of the manifest header and the name of the service property.
+ */
+ public static final String SERVICE_PROP_REQUIRE_TLD_BUNDLE = REQUIRE_TLD_BUNDLE;
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java
new file mode 100644
index 0000000000..4b48aef2a3
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java
@@ -0,0 +1,65 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.internal.jsp;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * Tricky url classloader. In fact we don't want a real URLClassLoader: we want
+ * OSGi to provide its classloader and let it does. But to let
+ * {@link org.apache.jasper.compiler.TldLocationsCache} find the core tlds
+ * inside the jars we must be a URLClassLoader that returns an array of jars
+ * where tlds are stored when the method getURLs is called.
+ */
+public class TldLocatableURLClassloader extends URLClassLoader
+{
+
+ private URL[] _jarsWithTldsInside;
+
+ public TldLocatableURLClassloader(ClassLoader osgiClassLoader, URL[] jarsWithTldsInside)
+ {
+ super(new URL[] {},osgiClassLoader);
+ _jarsWithTldsInside = jarsWithTldsInside;
+ }
+
+ /**
+ * @return the jars that contains tlds so that TldLocationsCache or
+ * TldScanner can find them.
+ */
+ @Override
+ public URL[] getURLs()
+ {
+ return _jarsWithTldsInside;
+ }
+
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder();
+
+ if (_jarsWithTldsInside != null)
+ {
+ for (URL u:_jarsWithTldsInside)
+ builder.append(" "+u.toString());
+ return builder.toString();
+ }
+ else
+ return super.toString();
+ }
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloaderWithInsertedJettyClassloader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloaderWithInsertedJettyClassloader.java
new file mode 100644
index 0000000000..e4cf805aac
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloaderWithInsertedJettyClassloader.java
@@ -0,0 +1,69 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.internal.jsp;
+
+import java.net.URL;
+
+/**
+ * Add a classloader to the
+ * org.apache.jasper.compiler.TldLocatableURLClassloader. Hopefuly not
+ * necessary: still experimenting.
+ *
+ * @see TldLocatableURLClassloader
+ */
+public class TldLocatableURLClassloaderWithInsertedJettyClassloader extends TldLocatableURLClassloader
+{
+
+ private ClassLoader _internalClassLoader;
+
+ /**
+ *
+ * @param osgiClassLoaderParent
+ * The parent classloader
+ * @param internalClassLoader
+ * The classloader that will be at the same level than the
+ * jarsWithTldsInside
+ * @param jarsWithTldsInside
+ * jars that are scanned for tld files.
+ */
+ public TldLocatableURLClassloaderWithInsertedJettyClassloader(ClassLoader osgiClassLoaderParent, ClassLoader internalClassLoader, URL[] jarsWithTldsInside)
+ {
+ super(osgiClassLoaderParent,jarsWithTldsInside);
+ _internalClassLoader = internalClassLoader;
+ }
+
+ protected Class<?> findClass(String name) throws ClassNotFoundException
+ {
+ try
+ {
+ return super.findClass(name);
+ }
+ catch (ClassNotFoundException cne)
+ {
+ if (_internalClassLoader != null)
+ {
+ return _internalClassLoader.loadClass(name);
+ }
+ else
+ {
+ throw cne;
+ }
+ }
+ }
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
new file mode 100644
index 0000000000..5624d07e62
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
@@ -0,0 +1,338 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.internal.serverfactory;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
+import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
+import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Called by the {@link JettyBootstrapActivator} during the starting of the
+ * bundle. If the system property 'jetty.home' is defined and points to a
+ * folder, then setup the corresponding jetty server.
+ */
+public class DefaultJettyAtJettyHomeHelper
+{
+ private static final Logger LOG = Log.getLogger(DefaultJettyAtJettyHomeHelper.class);
+
+ /**
+ * contains a comma separated list of pathes to the etc/jetty-*.xml files
+ * used to configure jetty. By default the value is 'etc/jetty.xml' when the
+ * path is relative the file is resolved relatively to jettyhome.
+ */
+ public static final String SYS_PROP_JETTY_ETC_FILES = OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS;
+
+ /**
+ * Usual system property used as the hostname for a typical jetty
+ * configuration.
+ */
+ public static final String SYS_PROP_JETTY_HOME = "jetty.home";
+
+ /**
+ * System property to point to a bundle that embeds a jetty configuration
+ * and that jetty configuration should be the default jetty server. First we
+ * look for jetty.home. If we don't find it then we look for this property.
+ */
+ public static final String SYS_PROP_JETTY_HOME_BUNDLE = "jetty.home.bundle";
+
+ /**
+ * Usual system property used as the hostname for a typical jetty
+ * configuration.
+ */
+ public static final String SYS_PROP_JETTY_HOST = "jetty.host";
+
+ /**
+ * Usual system property used as the port for http for a typical jetty
+ * configuration.
+ */
+ public static final String SYS_PROP_JETTY_PORT = "jetty.port";
+
+ /**
+ * Usual system property used as the port for https for a typical jetty
+ * configuration.
+ */
+ public static final String SYS_PROP_JETTY_PORT_SSL = "jetty.port.ssl";
+
+ /**
+ * Set of config files to apply to a jetty Server instance if none are supplied by SYS_PROP_JETTY_ETC_FILES
+ */
+ public static final String DEFAULT_JETTY_ETC_FILES = "jetty.xml,jetty-selector.xml,jetty-deployer.xml";
+
+ /**
+ * Default location within bundle of a jetty home dir.
+ */
+ public static final String DEFAULT_JETTYHOME = "/jettyhome";
+
+ /**
+ * Called by the JettyBootStrapActivator. If the system property jetty.home
+ * is defined and points to a folder, creates a corresponding jetty
+ * server.
+ * <p>
+ * If the system property jetty.home.bundle is defined and points to a
+ * bundle, look for the configuration of jetty inside that bundle.
+ * </p>
+ * <p>
+ * In both cases reads the system property 'jetty.etc.config.urls' to locate
+ * the configuration files for the deployed jetty. It is a comma separated
+ * list of URLs or relative paths inside the bundle or folder to the config
+ * files. If undefined it defaults to 'etc/jetty.xml'. In the case of the jetty.home.bundle,
+ * if no etc/jetty.xml file is found in the bundle, it will look for
+ * /jettyhome/etc/jetty-osgi-default.xml
+ * </p>
+ * <p>
+ * In both cases the system properties jetty.host, jetty.port and
+ * jetty.port.ssl are passed to the configuration files that might use them
+ * as part of their properties.
+ * </p>
+ */
+ public static void startJettyAtJettyHome(BundleContext bundleContext) throws Exception
+ {
+ String jettyHomeSysProp = System.getProperty(SYS_PROP_JETTY_HOME);
+ String jettyHomeBundleSysProp = System.getProperty(SYS_PROP_JETTY_HOME_BUNDLE);
+ File jettyHome = null;
+ Bundle jettyHomeBundle = null;
+ if (jettyHomeSysProp != null)
+ {
+ jettyHomeSysProp = resolvePropertyValue(jettyHomeSysProp);
+ // bug 329621
+ if (jettyHomeSysProp.startsWith("\"") && jettyHomeSysProp.endsWith("\"") || (jettyHomeSysProp.startsWith("'") && jettyHomeSysProp.endsWith("'")))
+ {
+ jettyHomeSysProp = jettyHomeSysProp.substring(1, jettyHomeSysProp.length() - 1);
+ }
+ if (jettyHomeBundleSysProp != null)
+ {
+ LOG.warn("Both jetty.home and jetty.home.bundle property defined: jetty.home.bundle ignored.");
+ }
+ jettyHome = new File(jettyHomeSysProp);
+ if (!jettyHome.exists() || !jettyHome.isDirectory())
+ {
+ LOG.warn("Unable to locate the jetty.home folder " + jettyHomeSysProp);
+ return;
+ }
+ }
+ else if (jettyHomeBundleSysProp != null)
+ {
+ jettyHomeBundleSysProp = resolvePropertyValue(jettyHomeBundleSysProp);
+ for (Bundle b : bundleContext.getBundles())
+ {
+ if (b.getSymbolicName().equals(jettyHomeBundleSysProp))
+ {
+ jettyHomeBundle = b;
+ break;
+ }
+ }
+ if (jettyHomeBundle == null)
+ {
+ LOG.warn("Unable to find the jetty.home.bundle named " + jettyHomeSysProp);
+ return;
+ }
+
+ }
+ if (jettyHome == null && jettyHomeBundle == null)
+ {
+ LOG.warn("No default jetty created.");
+ return;
+ }
+
+ Server server = new Server();
+ Dictionary<String,String> properties = new Hashtable<String,String>();
+ properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME);
+
+ String configURLs = jettyHome != null ? getJettyConfigurationURLs(jettyHome) : getJettyConfigurationURLs(jettyHomeBundle);
+ properties.put(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS, configURLs);
+
+ LOG.info("Configuring the default jetty server with " + configURLs);
+
+ // these properties usually are the ones passed to this type of
+ // configuration.
+ setProperty(properties, SYS_PROP_JETTY_HOME, System.getProperty(SYS_PROP_JETTY_HOME));
+ setProperty(properties, SYS_PROP_JETTY_HOST, System.getProperty(SYS_PROP_JETTY_HOST));
+ setProperty(properties, SYS_PROP_JETTY_PORT, System.getProperty(SYS_PROP_JETTY_PORT));
+ setProperty(properties, SYS_PROP_JETTY_PORT_SSL, System.getProperty(SYS_PROP_JETTY_PORT_SSL));
+
+ //register the Server instance as an OSGi service.
+ bundleContext.registerService(Server.class.getName(), server, properties);
+ // hookNestedConnectorToBridgeServlet(server);
+
+ }
+
+ /**
+ * Minimum setup for the location of the configuration files given a
+ * jettyhome folder. Reads the system property jetty.etc.config.urls and
+ * look for the corresponding jetty configuration files that will be used to
+ * setup the jetty server.
+ *
+ * @param jettyhome
+ * @return
+ */
+ private static String getJettyConfigurationURLs(File jettyhome)
+ {
+ String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES, "etc/jetty.xml");
+ StringTokenizer tokenizer = new StringTokenizer(jettyetc, ";,", false);
+ StringBuilder res = new StringBuilder();
+ while (tokenizer.hasMoreTokens())
+ {
+ String next = tokenizer.nextToken().trim();
+ if (!next.startsWith("/") && next.indexOf(':') == -1)
+ {
+ try
+ {
+ next = new File(jettyhome, next).toURI().toURL().toString();
+ }
+ catch (MalformedURLException e)
+ {
+ LOG.warn(e);
+ continue;
+ }
+ }
+ appendToCommaSeparatedList(res, next);
+ }
+ return res.toString();
+ }
+
+ /**
+ * Minimum setup for the location of the configuration files given a
+ * configuration embedded inside a bundle. Reads the system property
+ * jetty.etc.config.urls and look for the corresponding jetty configuration
+ * files that will be used to setup the jetty server.
+ *
+ * @param jettyhome
+ * @return
+ */
+ private static String getJettyConfigurationURLs(Bundle configurationBundle)
+ {
+ String files = System.getProperty(SYS_PROP_JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
+
+ StringTokenizer tokenizer = new StringTokenizer(files, ";,", false);
+ StringBuilder res = new StringBuilder();
+
+ while (tokenizer.hasMoreTokens())
+ {
+ String etcFile = tokenizer.nextToken().trim();
+ if (etcFile.startsWith("/") || etcFile.indexOf(":") != -1)
+ {
+ //file path is absolute
+ appendToCommaSeparatedList(res, etcFile);
+ }
+ else
+ {
+ //relative file path
+ Enumeration<URL> enUrls = BundleFileLocatorHelper.DEFAULT.findEntries(configurationBundle, etcFile);
+
+ // default for org.eclipse.osgi.boot where we look inside
+ // jettyhome for the default embedded configuration.
+ // default inside jettyhome. this way fragments to the bundle
+ // can define their own configuration.
+ if ((enUrls == null || !enUrls.hasMoreElements()))
+ {
+ String tmp = DEFAULT_JETTYHOME+"/etc/"+etcFile;
+ enUrls = BundleFileLocatorHelper.DEFAULT.findEntries(configurationBundle, tmp);
+ LOG.info("Configuring jetty with the default embedded configuration:" + "bundle: "
+ + configurationBundle.getSymbolicName()
+ + " config: "+tmp);
+ }
+ if (enUrls == null || !enUrls.hasMoreElements())
+ {
+ LOG.warn("Unable to locate a jetty configuration file for " + etcFile);
+ }
+ if (enUrls != null)
+ {
+ while (enUrls.hasMoreElements())
+ {
+ appendToCommaSeparatedList(res, enUrls.nextElement().toString());
+ }
+ }
+ }
+ }
+ return res.toString();
+ }
+
+ private static void appendToCommaSeparatedList(StringBuilder buffer, String value)
+ {
+ if (buffer.length() != 0)
+ {
+ buffer.append(",");
+ }
+ buffer.append(value);
+ }
+
+ private static void setProperty(Dictionary<String,String> properties, String key, String value)
+ {
+ if (value != null)
+ {
+ properties.put(key, value);
+ }
+ }
+
+ /**
+ * recursively substitute the ${sysprop} by their actual system property.
+ * ${sysprop,defaultvalue} will use 'defaultvalue' as the value if no
+ * sysprop is defined. Not the most efficient code but we are shooting for
+ * simplicity and speed of development here.
+ *
+ * @param value
+ * @return
+ */
+ public static String resolvePropertyValue(String value)
+ {
+ int ind = value.indexOf("${");
+ if (ind == -1) { return value; }
+ int ind2 = value.indexOf('}', ind);
+ if (ind2 == -1) { return value; }
+ String sysprop = value.substring(ind + 2, ind2);
+ String defaultValue = null;
+ int comma = sysprop.indexOf(',');
+ if (comma != -1 && comma + 1 != sysprop.length())
+ {
+ defaultValue = sysprop.substring(comma + 1);
+ defaultValue = resolvePropertyValue(defaultValue);
+ sysprop = sysprop.substring(0, comma);
+ }
+ else
+ {
+ defaultValue = "${" + sysprop + "}";
+ }
+
+ String v = System.getProperty(sysprop);
+
+ String reminder = value.length() > ind2 + 1 ? value.substring(ind2 + 1) : "";
+ reminder = resolvePropertyValue(reminder);
+ if (v != null)
+ {
+ return value.substring(0, ind) + v + reminder;
+ }
+ else
+ {
+ return value.substring(0, ind) + defaultValue + reminder;
+ }
+ }
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/IManagedJettyServerRegistry.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/IManagedJettyServerRegistry.java
new file mode 100644
index 0000000000..bb65462dff
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/IManagedJettyServerRegistry.java
@@ -0,0 +1,34 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.internal.serverfactory;
+
+/**
+ * Keeps track of the running jetty servers. They are named.
+ */
+public interface IManagedJettyServerRegistry
+{
+
+ /**
+ * @param managedServerName The server name
+ * @return the corresponding jetty server wrapped with its deployment
+ * properties.
+ */
+ public ServerInstanceWrapper getServerInstanceWrapper(String managedServerName);
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java
new file mode 100644
index 0000000000..e172c75197
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java
@@ -0,0 +1,172 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.internal.serverfactory;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Deploy the jetty server instances when they are registered as an OSGi
+ * service.
+ */
+public class JettyServerServiceTracker implements ServiceListener, IManagedJettyServerRegistry
+{
+ private static Logger LOG = Log.getLogger(JettyServerServiceTracker.class.getName());
+
+ /**
+ * Servers indexed by PIDs. PIDs are generated by the ConfigurationAdmin
+ * service.
+ */
+ private Map<String, ServerInstanceWrapper> _serversIndexedByName = new HashMap<String, ServerInstanceWrapper>();
+
+ /** The context-handler to deactivate indexed by ServerInstanceWrapper */
+ private Map<ServiceReference, ServerInstanceWrapper> _indexByServiceReference = new HashMap<ServiceReference, ServerInstanceWrapper>();
+
+ /**
+ * Stops each one of the registered servers.
+ */
+ public void stop()
+ {
+ // not sure that this is really useful but here we go.
+ for (ServerInstanceWrapper wrapper : _serversIndexedByName.values())
+ {
+ try
+ {
+ wrapper.stop();
+ }
+ catch (Throwable t)
+ {
+ LOG.warn(t);
+ }
+ }
+ }
+
+ /**
+ * Receives notification that a service has had a lifecycle change.
+ *
+ * @param ev The <code>ServiceEvent</code> object.
+ */
+ public void serviceChanged(ServiceEvent ev)
+ {
+ ServiceReference sr = ev.getServiceReference();
+ switch (ev.getType())
+ {
+ case ServiceEvent.MODIFIED:
+ case ServiceEvent.UNREGISTERING:
+ {
+ ServerInstanceWrapper instance = unregisterInIndex(ev.getServiceReference());
+ if (instance != null)
+ {
+ try
+ {
+ instance.stop();
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+
+ if (ev.getType() == ServiceEvent.UNREGISTERING)
+ {
+ break;
+ }
+ else
+ {
+ // modified, meaning: we reload it. now that we stopped it;
+ // we can register it.
+ }
+ }
+ case ServiceEvent.REGISTERED:
+ {
+ try
+ {
+ Bundle contributor = sr.getBundle();
+ Server server = (Server) contributor.getBundleContext().getService(sr);
+ ServerInstanceWrapper wrapper = registerInIndex(server, sr);
+ Properties props = new Properties();
+ for (String key : sr.getPropertyKeys())
+ {
+ Object value = sr.getProperty(key);
+ props.put(key, value);
+ }
+ wrapper.start(server, props);
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ }
+ break;
+ }
+ }
+ }
+
+ private ServerInstanceWrapper registerInIndex(Server server, ServiceReference sr)
+ {
+ String name = (String) sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
+ if (name == null) { throw new IllegalArgumentException("The property " + OSGiServerConstants.MANAGED_JETTY_SERVER_NAME + " is mandatory"); }
+ ServerInstanceWrapper wrapper = new ServerInstanceWrapper(name);
+ _indexByServiceReference.put(sr, wrapper);
+ _serversIndexedByName.put(name, wrapper);
+ return wrapper;
+ }
+
+ /**
+ * Returns the ContextHandler to stop.
+ *
+ * @param reg
+ * @return the ContextHandler to stop.
+ */
+ private ServerInstanceWrapper unregisterInIndex(ServiceReference sr)
+ {
+ ServerInstanceWrapper handler = _indexByServiceReference.remove(sr);
+ if (handler == null)
+ {
+ // a warning?
+ return null;
+ }
+ String name = handler.getManagedServerName();
+ if (name != null)
+ {
+ _serversIndexedByName.remove(name);
+ }
+ return handler;
+ }
+
+ /**
+ * @param managedServerName The server name
+ * @return the corresponding jetty server wrapped with its deployment
+ * properties.
+ */
+ public ServerInstanceWrapper getServerInstanceWrapper(String managedServerName)
+ {
+ return _serversIndexedByName.get(managedServerName == null ? OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME : managedServerName);
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java
new file mode 100644
index 0000000000..10f442f9c4
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java
@@ -0,0 +1,444 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.internal.serverfactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.eclipse.jetty.deploy.AppProvider;
+import org.eclipse.jetty.deploy.DeploymentManager;
+import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
+import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
+import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
+import org.eclipse.jetty.osgi.boot.internal.jsp.TldLocatableURLClassloader;
+import org.eclipse.jetty.osgi.boot.internal.webapp.LibExtClassLoaderHelper;
+import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleDeployerHelper;
+import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
+import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.xml.XmlConfiguration;
+import org.xml.sax.SAXParseException;
+
+/**
+ * ServerInstanceWrapper
+ *
+ * Configures and starts a jetty Server instance.
+ */
+public class ServerInstanceWrapper
+{
+
+ /**
+ * The value of this property points to the parent director of the jetty.xml
+ * configuration file currently executed. Everything is passed as a URL to
+ * support the case where the bundle is zipped.
+ */
+ public static final String PROPERTY_THIS_JETTY_XML_FOLDER_URL = "this.jetty.xml.parent.folder.url";
+
+ private static Logger LOG = Log.getLogger(ServerInstanceWrapper.class.getName());
+
+ private final String _managedServerName;
+
+ /**
+ * The managed jetty server
+ */
+ private Server _server;
+
+ private ContextHandlerCollection _ctxtHandler;
+
+ /**
+ * This is the class loader that should be the parent classloader of any
+ * webapp classloader. It is in fact the _libExtClassLoader with a trick to
+ * let the TldScanner find the jars where the tld files are.
+ */
+ private ClassLoader _commonParentClassLoaderForWebapps;
+
+ private DeploymentManager _deploymentManager;
+
+ private OSGiAppProvider _provider;
+
+ private WebBundleDeployerHelper _webBundleDeployerHelper;
+
+ public ServerInstanceWrapper(String managedServerName)
+ {
+ _managedServerName = managedServerName;
+ }
+
+ public String getManagedServerName()
+ {
+ return _managedServerName;
+ }
+
+ /**
+ * The classloader that should be the parent classloader for each webapp
+ * deployed on this server.
+ *
+ * @return
+ */
+ public ClassLoader getParentClassLoaderForWebapps()
+ {
+ return _commonParentClassLoaderForWebapps;
+ }
+
+ /**
+ * @return The deployment manager registered on this server.
+ */
+ public DeploymentManager getDeploymentManager()
+ {
+ return _deploymentManager;
+ }
+
+ /**
+ * @return The app provider registered on this server.
+ */
+ public OSGiAppProvider getOSGiAppProvider()
+ {
+ return _provider;
+ }
+
+ public Server getServer()
+ {
+ return _server;
+ }
+
+ public WebBundleDeployerHelper getWebBundleDeployerHelp()
+ {
+ return _webBundleDeployerHelper;
+ }
+
+ /**
+ * @return The collection of context handlers
+ */
+ public ContextHandlerCollection getContextHandlerCollection()
+ {
+ return _ctxtHandler;
+ }
+
+ public void start(Server server, Dictionary props) throws Exception
+ {
+ _server = server;
+ ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
+ try
+ {
+ // passing this bundle's classloader as the context classloader
+ // makes sure there is access to all the jetty's bundles
+ ClassLoader libExtClassLoader = null;
+ String sharedURLs = (String) props.get(OSGiServerConstants.MANAGED_JETTY_SHARED_LIB_FOLDER_URLS);
+
+ List<File> shared = sharedURLs != null ? extractFiles(sharedURLs) : null;
+ libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader(shared, null, server, JettyBootstrapActivator.class.getClassLoader());
+
+ Thread.currentThread().setContextClassLoader(libExtClassLoader);
+
+ configure(server, props);
+
+ init();
+
+ // now that we have an app provider we can call the registration
+ // customizer.
+
+ URL[] jarsWithTlds = getJarsWithTlds();
+ _commonParentClassLoaderForWebapps = jarsWithTlds == null ? libExtClassLoader : new TldLocatableURLClassloader(libExtClassLoader, jarsWithTlds);
+
+ server.start();
+ _webBundleDeployerHelper = new WebBundleDeployerHelper(this);
+ }
+ catch (Exception e)
+ {
+ if (server != null)
+ {
+ try
+ {
+ server.stop();
+ }
+ catch (Exception x)
+ {
+ LOG.ignore(x);
+ }
+ }
+ throw e;
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(contextCl);
+ }
+ }
+
+
+ public void stop()
+ {
+ try
+ {
+ if (_server.isRunning())
+ {
+ _server.stop();
+ }
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+
+ /**
+ * TODO: right now only the jetty-jsp bundle is scanned for common taglibs.
+ * Should support a way to plug more bundles that contain taglibs.
+ *
+ * The jasper TldScanner expects a URLClassloader to parse a jar for the
+ * /META-INF/*.tld it may contain. We place the bundles that we know contain
+ * such tag-libraries. Please note that it will work if and only if the
+ * bundle is a jar (!) Currently we just hardcode the bundle that contains
+ * the jstl implementation.
+ *
+ * A workaround when the tld cannot be parsed with this method is to copy
+ * and paste it inside the WEB-INF of the webapplication where it is used.
+ *
+ * Support only 2 types of packaging for the bundle: - the bundle is a jar
+ * (recommended for runtime.) - the bundle is a folder and contain jars in
+ * the root and/or in the lib folder (nice for PDE development situations)
+ * Unsupported: the bundle is a jar that embeds more jars.
+ *
+ * @return
+ * @throws Exception
+ */
+ private URL[] getJarsWithTlds() throws Exception
+ {
+ ArrayList<URL> res = new ArrayList<URL>();
+ WebBundleDeployerHelper.staticInit();// that is not looking great.
+ for (WebappRegistrationCustomizer regCustomizer : WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS)
+ {
+ URL[] urls = regCustomizer.getJarsWithTlds(_provider, WebBundleDeployerHelper.BUNDLE_FILE_LOCATOR_HELPER);
+ for (URL url : urls)
+ {
+ if (!res.contains(url)) res.add(url);
+ }
+ }
+ if (!res.isEmpty())
+ return res.toArray(new URL[res.size()]);
+ else
+ return null;
+ }
+
+ private void configure(Server server, Dictionary props) throws Exception
+ {
+ String jettyConfigurationUrls = (String) props.get(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS);
+ List<URL> jettyConfigurations = jettyConfigurationUrls != null ? extractResources(jettyConfigurationUrls) : null;
+ if (jettyConfigurations == null || jettyConfigurations.isEmpty()) { return; }
+ Map<String, Object> id_map = new HashMap<String, Object>();
+
+ //TODO need to put in the id of the server being configured
+ id_map.put("Server", server);
+ Map<String, String> properties = new HashMap<String, String>();
+ Enumeration<Object> en = props.keys();
+ while (en.hasMoreElements())
+ {
+ Object key = en.nextElement();
+ Object value = props.get(key);
+ properties.put(String.valueOf(key), String.valueOf(value));
+ }
+
+ for (URL jettyConfiguration : jettyConfigurations)
+ {
+ InputStream is = null;
+ try
+ {
+ // Execute a Jetty configuration file
+ Resource r = Resource.newResource(jettyConfiguration);
+ is = r.getInputStream();
+ XmlConfiguration config = new XmlConfiguration(is);
+ config.getIdMap().putAll(id_map);
+
+ // #334062 compute the URL of the folder that contains the
+ // jetty.xml conf file
+ // and set it as a property so we can compute relative paths
+ // from it.
+ String urlPath = jettyConfiguration.toString();
+ int lastSlash = urlPath.lastIndexOf('/');
+ if (lastSlash > 4)
+ {
+ urlPath = urlPath.substring(0, lastSlash);
+ Map<String, String> properties2 = new HashMap<String, String>(properties);
+ properties2.put(PROPERTY_THIS_JETTY_XML_FOLDER_URL, urlPath);
+ config.getProperties().putAll(properties2);
+ }
+ else
+ {
+ config.getProperties().putAll(properties);
+ }
+ config.configure();
+ id_map = config.getIdMap();
+ }
+ catch (SAXParseException saxparse)
+ {
+ LOG.warn("Unable to configure the jetty/etc file " + jettyConfiguration, saxparse);
+ throw saxparse;
+ }
+ finally
+ {
+ IO.close(is);
+ }
+ }
+
+ }
+
+ /**
+ * Must be called after the server is configured.
+ *
+ * It is assumed the server has already been configured with the ContextHandlerCollection structure.
+ *
+ * The server must have an instance of OSGiAppProvider. If one is not provided, it is created.
+ */
+ private void init()
+ {
+ // Get the context handler
+ _ctxtHandler = (ContextHandlerCollection) _server.getChildHandlerByClass(ContextHandlerCollection.class);
+
+ // get a deployerManager
+ Collection<DeploymentManager> deployers = _server.getBeans(DeploymentManager.class);
+ if (deployers != null && !deployers.isEmpty())
+ {
+ _deploymentManager = deployers.iterator().next();
+
+ for (AppProvider provider : _deploymentManager.getAppProviders())
+ {
+ if (provider instanceof OSGiAppProvider)
+ {
+ _provider = (OSGiAppProvider) provider;
+ break;
+ }
+ }
+ if (_provider == null)
+ {
+ // create it on the fly with reasonable default values.
+ try
+ {
+ _provider = new OSGiAppProvider();
+ _provider.setMonitoredDirResource(Resource.newResource(getDefaultOSGiContextsHome(new File(System.getProperty("jetty.home"))).toURI()));
+ }
+ catch (IOException e)
+ {
+ LOG.warn(e);
+ }
+ _deploymentManager.addAppProvider(_provider);
+ }
+ }
+
+ if (_ctxtHandler == null || _provider == null) throw new IllegalStateException("ERROR: No ContextHandlerCollection or OSGiAppProvider configured");
+
+ }
+
+ /**
+ * @return The default folder in which the context files of the osgi bundles
+ * are located and watched. Or null when the system property
+ * "jetty.osgi.contexts.home" is not defined. If the configuration
+ * file defines the OSGiAppProvider's context. This will not be
+ * taken into account.
+ */
+ File getDefaultOSGiContextsHome(File jettyHome)
+ {
+ String jettyContextsHome = System.getProperty("jetty.osgi.contexts.home");
+ if (jettyContextsHome != null)
+ {
+ File contextsHome = new File(jettyContextsHome);
+ if (!contextsHome.exists() || !contextsHome.isDirectory())
+ {
+ throw new IllegalArgumentException("the ${jetty.osgi.contexts.home} '"
+ + jettyContextsHome
+ + " must exist and be a folder");
+ }
+ return contextsHome;
+ }
+ return new File(jettyHome, "/contexts");
+ }
+
+ File getOSGiContextsHome()
+ {
+ return _provider.getContextXmlDirAsFile();
+ }
+
+ /**
+ * @return the urls in this string.
+ */
+ private List<URL> extractResources(String propertyValue)
+ {
+ StringTokenizer tokenizer = new StringTokenizer(propertyValue, ",;", false);
+ List<URL> urls = new ArrayList<URL>();
+ while (tokenizer.hasMoreTokens())
+ {
+ String tok = tokenizer.nextToken();
+ try
+ {
+ urls.add(((DefaultFileLocatorHelper) WebBundleDeployerHelper.BUNDLE_FILE_LOCATOR_HELPER).getLocalURL(new URL(tok)));
+ }
+ catch (Throwable mfe)
+ {
+ LOG.warn(mfe);
+ }
+ }
+ return urls;
+ }
+
+ /**
+ * Get the folders that might contain jars for the legacy J2EE shared
+ * libraries
+ */
+ private List<File> extractFiles(String propertyValue)
+ {
+ StringTokenizer tokenizer = new StringTokenizer(propertyValue, ",;", false);
+ List<File> files = new ArrayList<File>();
+ while (tokenizer.hasMoreTokens())
+ {
+ String tok = tokenizer.nextToken();
+ try
+ {
+ URL url = new URL(tok);
+ url = ((DefaultFileLocatorHelper) WebBundleDeployerHelper.BUNDLE_FILE_LOCATOR_HELPER).getFileURL(url);
+ if (url.getProtocol().equals("file"))
+ {
+ Resource res = Resource.newResource(url);
+ File folder = res.getFile();
+ if (folder != null)
+ {
+ files.add(folder);
+ }
+ }
+ }
+ catch (Throwable mfe)
+ {
+ LOG.warn(mfe);
+ }
+ }
+ return files;
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/IWebBundleDeployerHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/IWebBundleDeployerHelper.java
new file mode 100644
index 0000000000..502d972783
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/IWebBundleDeployerHelper.java
@@ -0,0 +1,87 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.internal.webapp;
+
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.osgi.framework.Bundle;
+
+/**
+ * Internal interface for the class that deploys a webapp on a server. Used as
+ * we migrate from the single instance of the jety server to multiple jetty
+ * servers.
+ */
+public interface IWebBundleDeployerHelper
+{
+
+ /**
+ * when this property is present, the type of context handler registered is
+ * not known in advance.
+ */
+ public static final String INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE = "unknownContextHandlerType";
+
+ /**
+ * Deploy a new web application on the jetty server.
+ *
+ * @param bundle The bundle
+ * @param webappFolderPath The path to the root of the webapp. Must be a
+ * path relative to bundle; either an absolute path.
+ * @param contextPath The context path. Must start with "/"
+ * @param extraClasspath
+ * @param overrideBundleInstallLocation
+ * @param requireTldBundle The list of bundles's symbolic names that contain
+ * tld files that are required by this WAB.
+ * @param webXmlPath
+ * @param defaultWebXmlPath TODO: parameter description
+ * @return The contexthandler created and started
+ * @throws Exception
+ */
+ public abstract WebAppContext registerWebapplication(Bundle bundle, String webappFolderPath, String contextPath, String extraClasspath,
+ String overrideBundleInstallLocation, String requireTldBundle, String webXmlPath,
+ String defaultWebXmlPath, WebAppContext webAppContext) throws Exception;
+
+ /**
+ * Stop a ContextHandler and remove it from the collection.
+ *
+ * @see ContextDeployer#undeploy
+ * @param contextHandler
+ * @throws Exception
+ */
+ public abstract void unregister(ContextHandler contextHandler) throws Exception;
+
+ /**
+ * This type of registration relies on jetty's complete context xml file.
+ * Context encompasses jndi and all other things. This makes the definition
+ * of the webapp a lot more self-contained.
+ *
+ * @param contributor
+ * @param contextFileRelativePath
+ * @param extraClasspath
+ * @param overrideBundleInstallLocation
+ * @param requireTldBundle The list of bundles'symbolic name that contain
+ * tld files for this webapp.
+ * @param handler the context handler passed in the server reference that
+ * will be configured, deployed and started.
+ * @return The contexthandler created and started
+ * @throws Exception
+ */
+ public abstract ContextHandler registerContext(Bundle contributor, String contextFileRelativePath, String extraClasspath,
+ String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler) throws Exception;
+
+} \ No newline at end of file
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java
new file mode 100644
index 0000000000..00dea2dfb7
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java
@@ -0,0 +1,390 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.internal.webapp;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
+import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
+import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.IManagedJettyServerRegistry;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.Scanner;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * When a {@link ContextHandler} service is activated we look into it and if the
+ * corresponding webapp is actually not configured then we go and register it.
+ * <p>
+ * The idea is to always go through this class when we deploy a new webapp on
+ * jetty.
+ * </p>
+ * <p>
+ * We are exposing each web-application as an OSGi service. This lets us update
+ * the webapps and stop/start them directly at the OSGi layer. It also give us
+ * many ways to declare those services: Declarative Services for example. <br/>
+ * It is a bit different from the way the HttpService works where we would have
+ * a WebappService and we woud register a webapp onto it. <br/>
+ * It does not go against RFC-66 nor does it prevent us from supporting the
+ * WebappContainer semantics.
+ * </p>
+ */
+public class JettyContextHandlerServiceTracker implements ServiceListener
+{
+ private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName());
+
+ /** New style: ability to manage multiple jetty instances */
+ private final IManagedJettyServerRegistry _registry;
+
+ /** The context-handler to deactivate indexed by context handler */
+ private Map<ServiceReference, ContextHandler> _indexByServiceReference = new HashMap<ServiceReference, ContextHandler>();
+
+ /**
+ * The index is the bundle-symbolic-name/path/to/context/file when there is
+ * such thing
+ */
+ private Map<String, ServiceReference> _indexByContextFile = new HashMap<String, ServiceReference>();
+
+ /** in charge of detecting changes in the osgi contexts home folder. */
+ private Scanner _scanner;
+
+ /**
+ * @param registry
+ */
+ public JettyContextHandlerServiceTracker(IManagedJettyServerRegistry registry) throws Exception
+ {
+ _registry = registry;
+ }
+
+ public void stop() throws Exception
+ {
+ if (_scanner != null)
+ {
+ _scanner.stop();
+ }
+ // the class that created the server is also in charge of stopping it.
+ // nothing to stop in the WebappRegistrationHelper
+
+ }
+
+ /**
+ * @param contextHome Parent folder where the context files can override the
+ * context files defined in the web bundles: equivalent to the
+ * contexts folder in a traditional jetty installation. when
+ * null, just do nothing.
+ */
+ protected void setupContextHomeScanner(File contextHome) throws IOException
+ {
+ if (contextHome == null) { return; }
+ final String osgiContextHomeFolderCanonicalPath = contextHome.getCanonicalPath();
+ _scanner = new Scanner();
+ _scanner.setRecursive(true);
+ _scanner.setReportExistingFilesOnStartup(false);
+ _scanner.addListener(new Scanner.DiscreteListener()
+ {
+ public void fileAdded(String filename) throws Exception
+ {
+ // adding a file does not create a new app,
+ // it just reloads it with the new custom file.
+ // well, if the file does not define a context handler,
+ // then in fact it does remove it.
+ reloadJettyContextHandler(filename, osgiContextHomeFolderCanonicalPath);
+ }
+
+ public void fileChanged(String filename) throws Exception
+ {
+ reloadJettyContextHandler(filename, osgiContextHomeFolderCanonicalPath);
+ }
+
+ public void fileRemoved(String filename) throws Exception
+ {
+ // removing a file does not remove the app:
+ // it just goes back to the default embedded in the bundle.
+ // well, if there was no default then it does remove it.
+ reloadJettyContextHandler(filename, osgiContextHomeFolderCanonicalPath);
+ }
+ });
+
+ }
+
+ /**
+ * Receives notification that a service has had a lifecycle change.
+ *
+ * @param ev The <code>ServiceEvent</code> object.
+ */
+ public void serviceChanged(ServiceEvent ev)
+ {
+ ServiceReference sr = ev.getServiceReference();
+ switch (ev.getType())
+ {
+ case ServiceEvent.MODIFIED:
+ case ServiceEvent.UNREGISTERING:
+ {
+ ContextHandler ctxtHandler = unregisterInIndex(ev.getServiceReference());
+ if (ctxtHandler != null && !ctxtHandler.isStopped())
+ {
+ try
+ {
+ getWebBundleDeployerHelp(sr).unregister(ctxtHandler);
+ }
+ catch (Exception e)
+ {
+ __logger.warn(e);
+ }
+ }
+ }
+ if (ev.getType() == ServiceEvent.UNREGISTERING)
+ {
+ break;
+ }
+ else
+ {
+ // modified, meaning: we reload it. now that we stopped it;
+ // we can register it.
+ }
+ case ServiceEvent.REGISTERED:
+ {
+ Bundle contributor = sr.getBundle();
+ BundleContext context = FrameworkUtil.getBundle(JettyBootstrapActivator.class).getBundleContext();
+ ContextHandler contextHandler = (ContextHandler) context.getService(sr);
+ if (contextHandler.getServer() != null)
+ {
+ // is configured elsewhere.
+ return;
+ }
+ String contextFilePath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
+ if (contextHandler instanceof WebAppContext && contextFilePath == null)
+ // it could be a web-application that will in fact be configured
+ // via a context file.
+ // that case is identified by the fact that the contextFilePath
+ // is not null
+ // in that case we must use the register context methods.
+ {
+ WebAppContext webapp = (WebAppContext) contextHandler;
+ String contextPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH);
+ if (contextPath == null)
+ {
+ contextPath = webapp.getContextPath();
+ }
+ String webXmlPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WEB_XML_PATH);
+ if (webXmlPath == null)
+ {
+ webXmlPath = webapp.getDescriptor();
+ }
+ String defaultWebXmlPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_DEFAULT_WEB_XML_PATH);
+ if (defaultWebXmlPath == null)
+ {
+ String jettyHome = System.getProperty(DefaultJettyAtJettyHomeHelper.SYS_PROP_JETTY_HOME);
+ if (jettyHome != null)
+ {
+ File etc = new File(jettyHome, "etc");
+ if (etc.exists() && etc.isDirectory())
+ {
+ File webDefault = new File(etc, "webdefault.xml");
+ if (webDefault.exists())
+ defaultWebXmlPath = webDefault.getAbsolutePath();
+ else
+ defaultWebXmlPath = webapp.getDefaultsDescriptor();
+ }
+ else
+ defaultWebXmlPath = webapp.getDefaultsDescriptor();
+ }
+ }
+ String war = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WAR);
+ try
+ {
+ IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr);
+ if (deployerHelper == null)
+ {
+
+ }
+ else
+ {
+ WebAppContext handler = deployerHelper.registerWebapplication(contributor,
+ war,
+ contextPath,
+ (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH),
+ (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE),
+ (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE),
+ webXmlPath, defaultWebXmlPath, webapp);
+ if (handler != null)
+ {
+ registerInIndex(handler, sr);
+ }
+ }
+ }
+ catch (Throwable e)
+ {
+ __logger.warn(e);
+ }
+ }
+ else
+ {
+ // consider this just an empty skeleton:
+ if (contextFilePath == null) { throw new IllegalArgumentException("the property contextFilePath is required"); }
+ try
+ {
+ IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr);
+ if (deployerHelper == null)
+ {
+ // more warnings?
+ }
+ else
+ {
+ if (Boolean.TRUE.toString().equals(sr.getProperty(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE)))
+ {
+ contextHandler = null;
+ }
+ ContextHandler handler = deployerHelper.registerContext(contributor,
+ contextFilePath,
+ (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH),
+ (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE),
+ (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE),
+ contextHandler);
+ if (handler != null)
+ {
+ registerInIndex(handler, sr);
+ }
+ }
+ }
+ catch (Throwable e)
+ {
+ __logger.warn(e);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ private void registerInIndex(ContextHandler handler, ServiceReference sr)
+ {
+ _indexByServiceReference.put(sr, handler);
+ String key = getSymbolicNameAndContextFileKey(sr);
+ if (key != null)
+ {
+ _indexByContextFile.put(key, sr);
+ }
+ }
+
+ /**
+ * Returns the ContextHandler to stop.
+ *
+ * @param reg
+ * @return the ContextHandler to stop.
+ */
+ private ContextHandler unregisterInIndex(ServiceReference sr)
+ {
+ ContextHandler handler = _indexByServiceReference.remove(sr);
+ String key = getSymbolicNameAndContextFileKey(sr);
+ if (key != null)
+ {
+ _indexByContextFile.remove(key);
+ }
+ if (handler == null)
+ {
+ // a warning?
+ return null;
+ }
+ return handler;
+ }
+
+ /**
+ * @param sr
+ * @return The key for a context file within the osgi contexts home folder.
+ */
+ private String getSymbolicNameAndContextFileKey(ServiceReference sr)
+ {
+ String contextFilePath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
+ if (contextFilePath != null) { return sr.getBundle().getSymbolicName() + "/" + contextFilePath; }
+ return null;
+ }
+
+ /**
+ * Called by the scanner when one of the context files is changed.
+ *
+ * @param contextFileFully
+ */
+ public void reloadJettyContextHandler(String canonicalNameOfFileChanged, String osgiContextHomeFolderCanonicalPath)
+ {
+ String key = getNormalizedRelativePath(canonicalNameOfFileChanged, osgiContextHomeFolderCanonicalPath);
+ if (key == null) { return; }
+ ServiceReference sr = _indexByContextFile.get(key);
+ if (sr == null)
+ {
+ // nothing to do?
+ return;
+ }
+ serviceChanged(new ServiceEvent(ServiceEvent.MODIFIED, sr));
+ }
+
+ /**
+ * @param canFilename
+ * @return
+ */
+ private String getNormalizedRelativePath(String canFilename, String osgiContextHomeFolderCanonicalPath)
+ {
+ if (!canFilename.startsWith(osgiContextHomeFolderCanonicalPath))
+ {
+ // why are we here: this does not look like a child of the osgi
+ // contexts home.
+ // warning?
+ return null;
+ }
+ return canFilename.substring(osgiContextHomeFolderCanonicalPath.length()).replace('\\', '/');
+ }
+
+ /**
+ * @return The server on which this webapp is meant to be deployed
+ */
+ private ServerInstanceWrapper getServerInstanceWrapper(String managedServerName)
+ {
+ if (_registry == null) { return null; }
+ if (managedServerName == null)
+ {
+ managedServerName = OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME;
+ }
+ ServerInstanceWrapper wrapper = _registry.getServerInstanceWrapper(managedServerName);
+ // System.err.println("Returning " + managedServerName + " = " +
+ // wrapper);
+ return wrapper;
+ }
+
+ private IWebBundleDeployerHelper getWebBundleDeployerHelp(ServiceReference sr)
+ {
+ if (_registry == null) { return null; }
+ String managedServerName = (String) sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
+ ServerInstanceWrapper wrapper = getServerInstanceWrapper(managedServerName);
+ return wrapper != null ? wrapper.getWebBundleDeployerHelp() : null;
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java
new file mode 100644
index 0000000000..c66fa26b12
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java
@@ -0,0 +1,213 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.internal.webapp;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jetty.server.Server;
+
+/**
+ * Helper to create a URL class-loader with the jars inside
+ * ${jetty.home}/lib/ext and ${jetty.home}/resources. In an ideal world, every
+ * library is an OSGi bundle that does loads nicely. To support standard jars or
+ * bundles that cannot be loaded in the current OSGi environment, we support
+ * inserting the jars in the usual jetty/lib/ext folders in the proper classpath
+ * for the webapps.
+ * <p>
+ * Also the folder resources typically contains central configuration files for
+ * things like: log config and others. We enable fragments to register classes
+ * that are called back and passed those resources to do what they need to do.
+ * </p>
+ * <p>
+ * For example the test-jndi webapplication depends on derby, derbytools,
+ * atomikos none of them are osgi bundles. we can either re-package them or we
+ * can place them in the usual lib/ext. <br/>
+ * In fact jasper's jsp libraries should maybe place in lib/ext too.
+ * </p>
+ * <p>
+ * The drawback is that those libraries will not be available in the OSGi
+ * classloader. Note that we could have setup those jars as embedded jars of the
+ * current bundle. However, we would need to know in advance what are those jars
+ * which was not acceptable. Also having those jars in a URLClassLoader seem to
+ * be required for some cases. For example jaspers' TldLocationsCache (replaced
+ * by TldScanner for servlet-3.0). <br/>
+ * Also all the dependencies of those libraries must be resolvable directly from
+ * the JettyBootstrapActivator bundle as it is set as the parent classloader. For
+ * example: if atomikos is placed in lib/ext it will work if and only if
+ * JettyBootstrapActivator import the necessary packages from javax.naming*,
+ * javax.transaction*, javax.mail* etc Most of the common cases of javax are
+ * added as optional import packages into jetty bootstrapper plugin. When there
+ * are not covered: please make a request or create a fragment or register a
+ * bundle with a buddy-policy onto the jetty bootstrapper..
+ * </p>
+ * <p>
+ * Alternatives to placing jars in lib/ext
+ * <ol>
+ * <li>Bundle the jars in an osgi bundle. Have the webapp(s) that context
+ * depends on them depend on that bundle. Things will go well for jetty.</li>
+ * <li>Bundle those jars in an osgi bundle-fragment that targets the
+ * jetty-bootstrap bundle</li>
+ * <li>Use equinox Buddy-Policy: register a buddy of the jetty bootstrapper
+ * bundle. (least favorite: it will work only on equinox)</li>
+ * </ol>
+ * </p>
+ */
+public class LibExtClassLoaderHelper
+{
+
+ /**
+ * Class called back
+ */
+ public interface IFilesInJettyHomeResourcesProcessor
+ {
+ void processFilesInResourcesFolder(File jettyHome, Map<String, File> filesInResourcesFolder);
+ }
+
+ public static Set<IFilesInJettyHomeResourcesProcessor> registeredFilesInJettyHomeResourcesProcessors = new HashSet<IFilesInJettyHomeResourcesProcessor>();
+
+ /**
+ * @param server
+ * @return a url classloader with the jars of resources, lib/ext and the
+ * jars passed in the other argument. The parent classloader usually
+ * is the JettyBootStrapper (an osgi classloader.
+ * @throws MalformedURLException
+ */
+ public static ClassLoader createLibEtcClassLoader(File jettyHome, Server server, ClassLoader parentClassLoader) throws MalformedURLException
+ {
+ if (jettyHome == null) { return parentClassLoader; }
+ ArrayList<URL> urls = new ArrayList<URL>();
+ File jettyResources = new File(jettyHome, "resources");
+ if (jettyResources.exists())
+ {
+ // make sure it contains something else than README:
+ Map<String, File> jettyResFiles = new HashMap<String, File>();
+ for (File f : jettyResources.listFiles())
+ {
+ jettyResFiles.put(f.getName(), f);
+ if (f.getName().toLowerCase(Locale.ENGLISH).startsWith("readme"))
+ {
+ continue;
+ }
+ else
+ {
+ if (urls.isEmpty())
+ {
+ urls.add(jettyResources.toURI().toURL());
+ }
+ }
+ }
+ processFilesInResourcesFolder(jettyHome, jettyResFiles);
+ }
+ File libExt = new File(jettyHome, "lib/ext");
+ if (libExt.exists())
+ {
+ for (File f : libExt.listFiles())
+ {
+ if (f.getName().endsWith(".jar"))
+ {
+ // cheap to tolerate folders so let's do it.
+ URL url = f.toURI().toURL();
+ if (f.isFile())
+ {// is this necessary anyways?
+ url = new URL("jar:" + url.toString() + "!/");
+ }
+ urls.add(url);
+ }
+ }
+ }
+
+ return new URLClassLoader(urls.toArray(new URL[urls.size()]), parentClassLoader);
+ }
+
+ /**
+ * @param server
+ * @return a url classloader with the jars of resources, lib/ext and the
+ * jars passed in the other argument. The parent classloader usually
+ * is the JettyBootStrapper (an osgi classloader). If there was no
+ * extra jars to insert, then just return the parentClassLoader.
+ * @throws MalformedURLException
+ */
+ public static ClassLoader createLibExtClassLoader(List<File> jarsContainerOrJars, List<URL> otherJarsOrFolder, Server server, ClassLoader parentClassLoader)
+ throws MalformedURLException
+ {
+ if (jarsContainerOrJars == null && otherJarsOrFolder == null) { return parentClassLoader; }
+ List<URL> urls = new ArrayList<URL>();
+ if (otherJarsOrFolder != null)
+ {
+ urls.addAll(otherJarsOrFolder);
+ }
+ if (jarsContainerOrJars != null)
+ {
+ for (File libExt : jarsContainerOrJars)
+ {
+ if (libExt.isDirectory())
+ {
+ for (File f : libExt.listFiles())
+ {
+ if (f.getName().endsWith(".jar"))
+ {
+ // cheap to tolerate folders so let's do it.
+ URL url = f.toURI().toURL();
+ if (f.isFile())
+ {
+ // is this necessary anyways?
+ url = new URL("jar:" + url.toString() + "!/");
+ }
+ urls.add(url);
+ }
+ }
+ }
+ }
+ }
+ return new URLClassLoader(urls.toArray(new URL[urls.size()]), parentClassLoader);
+ }
+
+ /**
+ * When we find files typically used for central logging configuration we do
+ * what it takes in this method to do what the user expects. Without
+ * depending too much directly on a particular logging framework.
+ * <p>
+ * We can afford to do some implementation specific code for a logging
+ * framework only in a fragment. <br/>
+ * Trying to configure log4j and logback in here.
+ * </p>
+ * <p>
+ * We recommend that slf4j jars are all placed in the osgi framework. And a
+ * single implementation if possible packaged as an osgi bundle is there.
+ * </p>
+ */
+ protected static void processFilesInResourcesFolder(File jettyHome, Map<String, File> childrenFiles)
+ {
+ for (IFilesInJettyHomeResourcesProcessor processor : registeredFilesInJettyHomeResourcesProcessors)
+ {
+ processor.processFilesInResourcesFolder(jettyHome, childrenFiles);
+ }
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java
new file mode 100644
index 0000000000..77b07a719d
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java
@@ -0,0 +1,285 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.internal.webapp;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.jar.JarFile;
+
+import javax.servlet.http.HttpServlet;
+
+import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelper;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.WebAppClassLoader;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
+
+/**
+ * Extends the webappclassloader to insert the classloader provided by the osgi
+ * bundle at the same level than any other jars palced in the webappclassloader.
+ */
+public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleReference
+{
+
+ private Logger __logger = Log.getLogger(OSGiWebappClassLoader.class.getName().toString());
+
+ /**
+ * when a logging framework is setup in the osgi classloaders, it can access
+ * this and register the classes that must not be found in the jar.
+ */
+ public static Set<String> JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED = new HashSet<String>();
+
+ public static void addClassThatIdentifiesAJarThatMustBeRejected(Class<?> zclass)
+ {
+ JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(zclass.getName().replace('.', '/') + ".class");
+ }
+
+ public static void addClassThatIdentifiesAJarThatMustBeRejected(String zclassName)
+ {
+ JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(zclassName.replace('.', '/') + ".class");
+ }
+
+ static
+ {
+ addClassThatIdentifiesAJarThatMustBeRejected(HttpServlet.class);
+ }
+
+ private ClassLoader _osgiBundleClassLoader;
+
+ private Bundle _contributor;
+
+ private boolean _lookInOsgiFirst = true;
+
+ private Set<String> _libsAlreadyInManifest = new HashSet<String>();
+
+ /**
+ * @param parent The parent classloader. In this case
+ * @param context The WebAppContext
+ * @param contributor The bundle that defines this web-application.
+ * @throws IOException
+ */
+ public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor, BundleClassLoaderHelper bundleClassLoaderHelper)
+ throws IOException
+ {
+ super(parent, context);
+ _contributor = contributor;
+ _osgiBundleClassLoader = bundleClassLoaderHelper.getBundleClassLoader(contributor);
+ }
+
+ /**
+ * Returns the <code>Bundle</code> that defined this web-application.
+ *
+ * @return The <code>Bundle</code> object associated with this
+ * <code>BundleReference</code>.
+ */
+ public Bundle getBundle()
+ {
+ return _contributor;
+ }
+
+ /**
+ * Reads the manifest. If the manifest is already configured to loads a few
+ * libs we should not add them to the classpath of the webapp. Not really
+ * important as we resolve classes through the osgi classloader first and
+ * then default on the libs of the webapp.
+ */
+ private void computeLibsAlreadyInOSGiClassLoader()
+ {
+ // TODO
+ }
+
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException
+ {
+ Enumeration<URL> osgiUrls = _osgiBundleClassLoader.getResources(name);
+ Enumeration<URL> urls = super.getResources(name);
+ if (_lookInOsgiFirst)
+ {
+ return Collections.enumeration(toList(osgiUrls, urls));
+ }
+ else
+ {
+ return Collections.enumeration(toList(urls, osgiUrls));
+ }
+ }
+
+ @Override
+ public URL getResource(String name)
+ {
+ if (_lookInOsgiFirst)
+ {
+ URL url = _osgiBundleClassLoader.getResource(name);
+ return url != null ? url : super.getResource(name);
+ }
+ else
+ {
+ URL url = super.getResource(name);
+ return url != null ? url : _osgiBundleClassLoader.getResource(name);
+ }
+ }
+
+ private List<URL> toList(Enumeration<URL> e, Enumeration<URL> e2)
+ {
+ List<URL> list = new ArrayList<URL>();
+ while (e != null && e.hasMoreElements())
+ list.add(e.nextElement());
+ while (e2 != null && e2.hasMoreElements())
+ list.add(e2.nextElement());
+ return list;
+ }
+
+ /**
+ *
+ */
+ protected Class<?> findClass(String name) throws ClassNotFoundException
+ {
+ try
+ {
+ return _lookInOsgiFirst ? _osgiBundleClassLoader.loadClass(name) : super.findClass(name);
+ }
+ catch (ClassNotFoundException cne)
+ {
+ try
+ {
+ return _lookInOsgiFirst ? super.findClass(name) : _osgiBundleClassLoader.loadClass(name);
+ }
+ catch (ClassNotFoundException cne2)
+ {
+ throw cne;
+ }
+ }
+ }
+
+ /**
+ * Parse the classpath ourselves to be able to filter things. This is a
+ * derivative work of the super class
+ */
+ @Override
+ public void addClassPath(String classPath) throws IOException
+ {
+
+ StringTokenizer tokenizer = new StringTokenizer(classPath, ",;");
+ while (tokenizer.hasMoreTokens())
+ {
+ String path = tokenizer.nextToken();
+ Resource resource = getContext().newResource(path);
+
+ // Resolve file path if possible
+ File file = resource.getFile();
+ if (file != null && isAcceptableLibrary(file, JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED))
+ {
+ super.addClassPath(path);
+ }
+ else
+ {
+ __logger.info("Did not add " + path + " to the classloader of the webapp " + getContext());
+ }
+ }
+
+ }
+
+ /**
+ * @param lib
+ * @return true if the lib should be included in the webapp classloader.
+ */
+ private boolean isAcceptableLibrary(File file, Set<String> pathToClassFiles)
+ {
+ try
+ {
+ if (file.isDirectory())
+ {
+ for (String criteria : pathToClassFiles)
+ {
+ if (new File(file, criteria).exists()) { return false; }
+ }
+ }
+ else
+ {
+ JarFile jar = null;
+ try
+ {
+ jar = new JarFile(file);
+ for (String criteria : pathToClassFiles)
+ {
+ if (jar.getEntry(criteria) != null) { return false; }
+ }
+ }
+ finally
+ {
+ if (jar != null) try
+ {
+ jar.close();
+ }
+ catch (IOException ioe)
+ {
+ }
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ // nevermind. just trying our best
+ __logger.ignore(e);
+ }
+ return true;
+ }
+
+ private static Field _contextField;
+
+ /**
+ * In the case of the generation of a webapp via a jetty context file we
+ * need a proper classloader to setup the app before we have the
+ * WebappContext So we place a fake one there to start with. We replace it
+ * with the actual webapp context with this method. We also apply the
+ * extraclasspath there at the same time.
+ */
+ public void setWebappContext(WebAppContext webappContext)
+ {
+ try
+ {
+ if (_contextField == null)
+ {
+ _contextField = WebAppClassLoader.class.getDeclaredField("_context");
+ _contextField.setAccessible(true);
+ }
+ _contextField.set(this, webappContext);
+ if (webappContext.getExtraClasspath() != null)
+ {
+ addClassPath(webappContext.getExtraClasspath());
+ }
+ }
+ catch (Throwable t)
+ {
+ // humf that will hurt if it does not work.
+ __logger.warn("Unable to set webappcontext", t);
+ }
+ }
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java
index 7ed6b7e81e..7ed6b7e81e 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java
new file mode 100644
index 0000000000..db976358dd
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java
@@ -0,0 +1,271 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.internal.webapp;
+
+
+import java.net.URL;
+import java.util.Dictionary;
+
+import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
+import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+
+/**
+ * Support bundles that declare the webapp directly through headers in their
+ * manifest.
+ * <p>
+ * Those headers will define a new WebApplication:
+ * <ul>
+ * <li>Web-ContextPath</li>
+ * <li>Jetty-WarFolderPath</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Those headers will define a new app started via a jetty-context or a list of
+ * them. ',' column is the separator between the various context files.
+ * <ul>
+ * <li>Jetty-ContextFilePath</li>
+ * </ul>
+ * </p>
+ * And generate a jetty WebAppContext or another ContextHandler then registers
+ * it as service. Kind of simpler than declarative services and their xml files.
+ * Also avoid having the contributing bundle depend on jetty's package for
+ * WebApp.
+ *
+ * @author hmalphettes
+ */
+public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
+{
+ private static final Logger LOG = Log.getLogger(WebBundleTrackerCustomizer.class);
+
+ /**
+ * A bundle is being added to the <code>BundleTracker</code>.
+ *
+ * <p>
+ * This method is called before a bundle which matched the search parameters
+ * of the <code>BundleTracker</code> is added to the
+ * <code>BundleTracker</code>. This method should return the object to be
+ * tracked for the specified <code>Bundle</code>. The returned object is
+ * stored in the <code>BundleTracker</code> and is available from the
+ * {@link BundleTracker#getObject(Bundle) getObject} method.
+ *
+ * @param bundle The <code>Bundle</code> being added to the
+ * <code>BundleTracker</code>.
+ * @param event The bundle event which caused this customizer method to be
+ * called or <code>null</code> if there is no bundle event
+ * associated with the call to this method.
+ * @return The object to be tracked for the specified <code>Bundle</code>
+ * object or <code>null</code> if the specified <code>Bundle</code>
+ * object should not be tracked.
+ */
+ public Object addingBundle(Bundle bundle, BundleEvent event)
+ {
+ if (bundle.getState() == Bundle.ACTIVE)
+ {
+ boolean isWebBundle = register(bundle);
+ return isWebBundle ? bundle : null;
+ }
+ else if (bundle.getState() == Bundle.STOPPING)
+ {
+ unregister(bundle);
+ }
+ else
+ {
+ // we should not be called in that state as
+ // we are registered only for ACTIVE and STOPPING
+ }
+ return null;
+ }
+
+ /**
+ * A bundle tracked by the <code>BundleTracker</code> has been modified.
+ *
+ * <p>
+ * This method is called when a bundle being tracked by the
+ * <code>BundleTracker</code> has had its state modified.
+ *
+ * @param bundle The <code>Bundle</code> whose state has been modified.
+ * @param event The bundle event which caused this customizer method to be
+ * called or <code>null</code> if there is no bundle event
+ * associated with the call to this method.
+ * @param object The tracked object for the specified bundle.
+ */
+ public void modifiedBundle(Bundle bundle, BundleEvent event, Object object)
+ {
+ // nothing the web-bundle was already track. something changed.
+ // we only reload the webapps if the bundle is stopped and restarted.
+ if (bundle.getState() == Bundle.STOPPING || bundle.getState() == Bundle.ACTIVE)
+ {
+ unregister(bundle);
+ }
+ if (bundle.getState() == Bundle.ACTIVE)
+ {
+ register(bundle);
+ }
+ }
+
+ /**
+ * A bundle tracked by the <code>BundleTracker</code> has been removed.
+ *
+ * <p>
+ * This method is called after a bundle is no longer being tracked by the
+ * <code>BundleTracker</code>.
+ *
+ * @param bundle The <code>Bundle</code> that has been removed.
+ * @param event The bundle event which caused this customizer method to be
+ * called or <code>null</code> if there is no bundle event
+ * associated with the call to this method.
+ * @param object The tracked object for the specified bundle.
+ */
+ public void removedBundle(Bundle bundle, BundleEvent event, Object object)
+ {
+ unregister(bundle);
+ }
+
+ /**
+ * @param bundle
+ * @return true if this bundle in indeed a web-bundle.
+ */
+ private boolean register(Bundle bundle)
+ {
+ Dictionary<?, ?> dic = bundle.getHeaders();
+ String warFolderRelativePath = (String) dic.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
+ if (warFolderRelativePath != null)
+ {
+ String contextPath = getWebContextPath(bundle, dic, false);
+ if (contextPath == null || !contextPath.startsWith("/"))
+ {
+ LOG.warn("The manifest header '" + OSGiWebappConstants.JETTY_WAR_FOLDER_PATH
+ + ": "
+ + warFolderRelativePath
+ + "' in the bundle "
+ + bundle.getSymbolicName()
+ + " is not valid: there is no Web-ContextPath defined in the manifest.");
+ return false;
+ }
+ // create the corresponding service and publish it in the context of
+ // the contributor bundle.
+ try
+ {
+ JettyBootstrapActivator.registerWebapplication(bundle, warFolderRelativePath, contextPath);
+ return true;
+ }
+ catch (Throwable e)
+ {
+ LOG.warn("Starting the web-bundle " + bundle.getSymbolicName() + " threw an exception.", e);
+ return true;// maybe it did not work maybe it did. safer to track this bundle.
+ }
+ }
+ else if (dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH) != null)
+ {
+ String contextFileRelativePath = (String) dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
+ if (contextFileRelativePath == null)
+ {
+ // nothing to register here.
+ return false;
+ }
+ // support for multiple webapps in the same bundle:
+ String[] pathes = contextFileRelativePath.split(",;");
+ for (String path : pathes)
+ {
+ try
+ {
+ JettyBootstrapActivator.registerContext(bundle, path.trim());
+ }
+ catch (Throwable e)
+ {
+ LOG.warn(e);
+ }
+ }
+ return true;
+ }
+ else
+ {
+ // support for OSGi-RFC66; disclaimer, no access to the actual
+ // (draft) of the spec: just a couple of posts on the
+ // world-wide-web.
+ URL rfc66Webxml = bundle.getEntry("/WEB-INF/web.xml");
+ if (rfc66Webxml == null && dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) == null)
+ {
+ return false;// no webapp in here
+ }
+ // this is risky: should we make sure that there is no classes and
+ // jars directly available
+ // at the root of the of the bundle: otherwise they are accessible
+ // through the browser. we should enforce that the whole classpath
+ // is
+ // pointing to files and folders inside WEB-INF. We should
+ // filter-out
+ // META-INF too
+ String rfc66ContextPath = getWebContextPath(bundle, dic, rfc66Webxml == null);
+ try
+ {
+ JettyBootstrapActivator.registerWebapplication(bundle, ".", rfc66ContextPath);
+ return true;
+ }
+ catch (Throwable e)
+ {
+ LOG.warn(e);
+ return true;// maybe it did not work maybe it did. safer to track this bundle.
+ }
+ }
+ }
+
+ private String getWebContextPath(Bundle bundle, Dictionary<?, ?> dic, boolean webinfWebxmlExists)
+ {
+ String rfc66ContextPath = (String) dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
+ if (rfc66ContextPath == null)
+ {
+ if (!webinfWebxmlExists) { return null; }
+ // extract from the last token of the bundle's location:
+ // (really ?
+ // could consider processing the symbolic name as an alternative
+ // the location will often reflect the version.
+ // maybe this is relevant when the file is a war)
+ String location = bundle.getLocation();
+ String toks[] = location.replace('\\', '/').split("/");
+ rfc66ContextPath = toks[toks.length - 1];
+ // remove .jar, .war etc:
+ int lastDot = rfc66ContextPath.lastIndexOf('.');
+ if (lastDot != -1)
+ {
+ rfc66ContextPath = rfc66ContextPath.substring(0, lastDot);
+ }
+ }
+ if (!rfc66ContextPath.startsWith("/"))
+ {
+ rfc66ContextPath = "/" + rfc66ContextPath;
+ }
+ return rfc66ContextPath;
+ }
+
+ private void unregister(Bundle bundle)
+ {
+ // nothing to do: when the bundle is stopped, each one of its service
+ // reference is also stopped and that is what we use to stop the
+ // corresponding
+ // webapps registered in that bundle.
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelper.java
new file mode 100644
index 0000000000..4c4382f3d4
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelper.java
@@ -0,0 +1,54 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.utils;
+
+import org.eclipse.jetty.osgi.boot.utils.internal.DefaultBundleClassLoaderHelper;
+import org.osgi.framework.Bundle;
+
+/**
+ * Is there a clean OSGi way to go from the Bundle object to the classloader of
+ * the Bundle ? You can certainly take a class inside the bundle and get the
+ * bundle's classloader that way. Getting the classloader directly from the
+ * bundle would be nice.
+ * <p>
+ * We could use fragments that are specific to each OSGi implementation. Using
+ * introspection here to keep packaging simple and avoid the multiplication of
+ * the jars.
+ * </p>
+ * <p>
+ * The default implementation relies on introspection and supports equinox-3.5
+ * and felix-2.0.0
+ * </p>
+ */
+public interface BundleClassLoaderHelper
+{
+
+ /** The name of the custom implementation for this interface in a fragment. */
+ public static final String CLASS_NAME = "org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelperImpl";
+
+ /** The default instance supports felix and equinox */
+ public static BundleClassLoaderHelper DEFAULT = new DefaultBundleClassLoaderHelper();
+
+ /**
+ * @return The classloader of a given bundle. Assuming the bundle is
+ * started.
+ */
+ public ClassLoader getBundleClassLoader(Bundle bundle);
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java
new file mode 100644
index 0000000000..ebba3dbd6e
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java
@@ -0,0 +1,93 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.utils;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
+import org.osgi.framework.Bundle;
+
+/**
+ * From a bundle to its location on the filesystem. Assumes the bundle is not a
+ * jar.
+ *
+ * @author hmalphettes
+ */
+public interface BundleFileLocatorHelper
+{
+
+ /** The name of the custom implementation for this interface in a fragment. */
+ public static final String CLASS_NAME = "org.eclipse.jetty.osgi.boot.utils.FileLocatorHelperImpl";
+
+ /** The default instance supports felix and equinox */
+ public static BundleFileLocatorHelper DEFAULT = new DefaultFileLocatorHelper();
+
+ /**
+ * Works with equinox, felix, nuxeo and probably more. Not exactly in the
+ * spirit of OSGi but quite necessary to support self-contained webapps and
+ * other situations.
+ * <p>
+ * Currently only works with bundles that are not jar.
+ * </p>
+ *
+ * @param bundle The bundle
+ * @return Its installation location as a file.
+ * @throws Exception
+ */
+ public File getBundleInstallLocation(Bundle bundle) throws Exception;
+
+ /**
+ * Locate a file inside a bundle.
+ *
+ * @param bundle
+ * @param path
+ * @return file object
+ * @throws Exception
+ */
+ public File getFileInBundle(Bundle bundle, String path) throws Exception;
+
+ /**
+ * If the bundle is a jar, returns the jar. If the bundle is a folder, look
+ * inside it and search for jars that it returns.
+ * <p>
+ * Good enough for our purpose (TldLocationsCache when it scans for tld
+ * files inside jars alone. In fact we only support the second situation for
+ * development purpose where the bundle was imported in pde and the classes
+ * kept in a jar.
+ * </p>
+ *
+ * @param bundle
+ * @return The jar(s) file that is either the bundle itself, either the jars
+ * embedded inside it.
+ */
+ public File[] locateJarsInsideBundle(Bundle bundle) throws Exception;
+
+ /**
+ * Helper method equivalent to Bundle#getEntry(String entryPath) except that
+ * it searches for entries in the fragments by using the findEntries method.
+ *
+ * @param bundle
+ * @param entryPath
+ * @return null or all the entries found for that path.
+ */
+ public Enumeration<URL> findEntries(Bundle bundle, String entryPath);
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java
new file mode 100644
index 0000000000..951443f062
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java
@@ -0,0 +1,60 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.utils;
+
+import java.net.URL;
+
+import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
+
+/**
+ * Fix various shortcomings with the way jasper parses the tld files.
+ */
+public interface WebappRegistrationCustomizer
+{
+ /**
+ * we could do something a lot more pluggable with a custom header in the
+ * manifest or some customer declarative services let's keep it simple for
+ * now. hopefully the rest of the world won't need to customize this.
+ */
+ public static final String CLASS_NAME = "org.eclipse.jetty.osgi.boot.jasper.WebappRegistrationCustomizerImpl";
+
+ /**
+ * TODO: right now only the jetty-jsp bundle is scanned for common taglibs.
+ * Should support a way to plug more bundles that contain taglibs.
+ *
+ * The jasper TldScanner expects a URLClassloader to parse a jar for the
+ * /META-INF/*.tld it may contain. We place the bundles that we know contain
+ * such tag-libraries. Please note that it will work if and only if the
+ * bundle is a jar (!) Currently we just hardcode the bundle that contains
+ * the jstl implemenation.
+ *
+ * A workaround when the tld cannot be parsed with this method is to copy
+ * and paste it inside the WEB-INF of the webapplication where it is used.
+ *
+ * Support only 2 types of packaging for the bundle: - the bundle is a jar
+ * (recommended for runtime.) - the bundle is a folder and contain jars in
+ * the root and/or in the lib folder (nice for PDE developement situations)
+ * Unsupported: the bundle is a jar that embeds more jars.
+ *
+ * @return array of URLs
+ * @throws Exception
+ */
+ URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper fileLocator) throws Exception;
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
new file mode 100644
index 0000000000..b1cfcc4897
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
@@ -0,0 +1,242 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.utils.internal;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelper;
+import org.osgi.framework.Bundle;
+
+/**
+ * Default implementation of the BundleClassLoaderHelper. Uses introspection to
+ * support equinox-3.5 and felix-2.0.0
+ */
+public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
+{
+
+ private static boolean identifiedOsgiImpl = false;
+
+ private static Class BundleWiringClass = null;
+ private static Method BundleWiringClass_getClassLoader_method = null;
+ private static Method BundleClass_adapt_method = null;
+
+ private static boolean isEquinox = false;
+
+ private static boolean isFelix = false;
+
+ private static void init(Bundle bundle)
+ {
+ identifiedOsgiImpl = true;
+
+ try
+ {
+ BundleWiringClass = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring");
+ if (BundleWiringClass != null)
+ {
+ BundleWiringClass_getClassLoader_method = BundleWiringClass.getDeclaredMethod("getClassLoader", new Class[] {});
+ BundleClass_adapt_method = bundle.getClass().getDeclaredMethod("adapt", new Class[] { Class.class });
+ BundleClass_adapt_method.setAccessible(true);
+ return;
+ }
+ }
+ catch (Throwable t)
+ {
+ //nevermind: an older version of OSGi where BundleWiring is not availble
+ //t.printStackTrace();
+ }
+
+ if (!bundle.getClass().getName().startsWith("org.apache.felix"))
+ {
+ try
+ {
+ isEquinox = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost") != null;
+ }
+ catch (Throwable t)
+ {
+ isEquinox = false;
+ }
+ }
+ if (!isEquinox)
+ {
+ try
+ {
+ isFelix = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl") != null;
+ }
+ catch (Throwable t2)
+ {
+ isFelix = false;
+ }
+ }
+ }
+
+ /**
+ * Assuming the bundle is started.
+ *
+ * @param bundle
+ * @return classloader object
+ */
+ public ClassLoader getBundleClassLoader(Bundle bundle)
+ {
+ //Older OSGi implementations:
+ String bundleActivator = (String) bundle.getHeaders().get("Bundle-Activator");
+ if (bundleActivator == null)
+ {
+ bundleActivator = (String) bundle.getHeaders().get("Jetty-ClassInBundle");
+ }
+ if (bundleActivator != null)
+ {
+ try
+ {
+ return bundle.loadClass(bundleActivator).getClassLoader();
+ }
+ catch (ClassNotFoundException e)
+ {
+ // should not happen as we are called if the bundle is started
+ // anyways.
+ e.printStackTrace();
+ }
+ }
+ // resort to introspection
+ if (!identifiedOsgiImpl)
+ {
+ init(bundle);
+ }
+ //This works for OSGi 4.2 and more recent. Aka version 1.6
+ //It is using ava reflection to execute:
+ //(BundleClassLoader) bundle.adapt(BundleWiring.class).getClassLoader()
+ if (BundleClass_adapt_method != null && BundleWiringClass_getClassLoader_method != null)
+ {
+ try
+ {
+ Object bundleWiring = BundleClass_adapt_method.invoke(bundle, BundleWiringClass);
+ return (ClassLoader)BundleWiringClass_getClassLoader_method.invoke(bundleWiring, new Object[] {});
+ }
+ catch (Throwable t)
+ {
+ t.printStackTrace();
+ return null;
+ }
+ }
+ if (isEquinox)
+ {
+ return internalGetEquinoxBundleClassLoader(bundle);
+ }
+ else if (isFelix) { return internalGetFelixBundleClassLoader(bundle); }
+ return null;
+ }
+
+ private static Method Equinox_BundleHost_getBundleLoader_method;
+
+ private static Method Equinox_BundleLoader_createClassLoader_method;
+
+ private static ClassLoader internalGetEquinoxBundleClassLoader(Bundle bundle)
+ {
+ // assume equinox:
+ try
+ {
+ if (Equinox_BundleHost_getBundleLoader_method == null)
+ {
+ Equinox_BundleHost_getBundleLoader_method =
+ bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost").getDeclaredMethod("getBundleLoader", new Class[] {});
+ Equinox_BundleHost_getBundleLoader_method.setAccessible(true);
+ }
+ Object bundleLoader = Equinox_BundleHost_getBundleLoader_method.invoke(bundle, new Object[] {});
+ if (Equinox_BundleLoader_createClassLoader_method == null && bundleLoader != null)
+ {
+ Equinox_BundleLoader_createClassLoader_method =
+ bundleLoader.getClass().getClassLoader().loadClass("org.eclipse.osgi.internal.loader.BundleLoader").getDeclaredMethod("createClassLoader", new Class[] {});
+ Equinox_BundleLoader_createClassLoader_method.setAccessible(true);
+ }
+ return (ClassLoader) Equinox_BundleLoader_createClassLoader_method.invoke(bundleLoader, new Object[] {});
+ }
+ catch (Throwable t)
+ {
+ t.printStackTrace();
+ }
+ return null;
+ }
+
+ private static Field Felix_BundleImpl_m_modules_field;
+
+ private static Field Felix_ModuleImpl_m_classLoader_field;
+
+ private static Field Felix_BundleImpl_m_revisions_field;
+
+
+ private static ClassLoader internalGetFelixBundleClassLoader(Bundle bundle)
+ {
+ // assume felix:
+ try
+ {
+ // now get the current module from the bundle.
+ // and return the private field m_classLoader of ModuleImpl
+ if (Felix_BundleImpl_m_modules_field == null)
+ {
+ Felix_BundleImpl_m_modules_field = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl").getDeclaredField("m_modules");
+ Felix_BundleImpl_m_modules_field.setAccessible(true);
+ }
+
+ // Figure out which version of the modules is exported
+ Object currentModuleImpl;
+ try
+ {
+ Object[] moduleArray = (Object[]) Felix_BundleImpl_m_modules_field.get(bundle);
+ currentModuleImpl = moduleArray[moduleArray.length - 1];
+ }
+ catch (Throwable t2)
+ {
+ @SuppressWarnings("unchecked")
+ List<Object> moduleArray = (List<Object>) Felix_BundleImpl_m_modules_field.get(bundle);
+ currentModuleImpl = moduleArray.get(moduleArray.size() - 1);
+ }
+
+ if (Felix_ModuleImpl_m_classLoader_field == null && currentModuleImpl != null)
+ {
+ Felix_ModuleImpl_m_classLoader_field = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.ModuleImpl").getDeclaredField("m_classLoader");
+ Felix_ModuleImpl_m_classLoader_field.setAccessible(true);
+ }
+ // first make sure that the classloader is ready:
+ // the m_classLoader field must be initialized by the
+ // ModuleImpl.getClassLoader() private method.
+ ClassLoader cl = (ClassLoader) Felix_ModuleImpl_m_classLoader_field.get(currentModuleImpl);
+ if (cl == null)
+ {
+ // looks like it was not ready:
+ // the m_classLoader field must be initialized by the
+ // ModuleImpl.getClassLoader() private method.
+ // this call will do that.
+ bundle.loadClass("java.lang.Object");
+ cl = (ClassLoader) Felix_ModuleImpl_m_classLoader_field.get(currentModuleImpl);
+ return cl;
+ }
+ else
+ {
+ return cl;
+ }
+ }
+ catch (Throwable t)
+ {
+ t.printStackTrace();
+ }
+ return null;
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
new file mode 100644
index 0000000000..3028aa6c2c
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
@@ -0,0 +1,446 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.utils.internal;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.zip.ZipFile;
+
+import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.resource.FileResource;
+import org.eclipse.jetty.util.resource.Resource;
+import org.osgi.framework.Bundle;
+
+/**
+ * From a bundle to its location on the filesystem.
+ * Often assumes the bundle is not a jar.
+ *
+ * @author hmalphettes
+ */
+public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
+{
+
+ // hack to locate the file-system directly from the bundle.
+ // support equinox, felix and nuxeo's osgi implementations.
+ // not tested on nuxeo and felix just yet.
+ // The url nuxeo and felix return is created directly from the File so it
+ // should work.
+ private static Field BUNDLE_ENTRY_FIELD = null;
+
+ private static Field FILE_FIELD = null;
+
+ private static Field BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY = null;// ZipBundleFile
+
+ // inside
+ // DirZipBundleEntry
+
+ private static Field ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = null;// ZipFile
+
+ /**
+ * Works with equinox, felix, nuxeo and probably more. Not exactly in the
+ * spirit of OSGi but quite necessary to support self-contained webapps and
+ * other situations.
+ *
+ * @param bundle The bundle
+ * @return Its installation location as a file.
+ * @throws Exception
+ */
+ public File getBundleInstallLocation(Bundle bundle) throws Exception
+ {
+ // String installedBundles = System.getProperty("osgi.bundles");
+ // grab the MANIFEST.MF's url
+ // and then do what it takes.
+ URL url = bundle.getEntry("/META-INF/MANIFEST.MF");
+
+ if (url.getProtocol().equals("file"))
+ {
+ // some osgi frameworks do use the file protocole directly in some
+ // situations. Do use the FileResource to transform the URL into a
+ // File: URL#toURI is broken
+ return new FileResource(url).getFile().getParentFile().getParentFile();
+ }
+ else if (url.getProtocol().equals("bundleentry"))
+ {
+ // say hello to equinox who has its own protocol.
+ // we use introspection like there is no tomorrow to get access to
+ // the File
+
+ URLConnection con = url.openConnection();
+ con.setUseCaches(Resource.getDefaultUseCaches()); // work around
+ // problems where
+ // url connections
+ // cache
+ // references to
+ // jars
+
+ if (BUNDLE_ENTRY_FIELD == null)
+ {
+ BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry");
+ BUNDLE_ENTRY_FIELD.setAccessible(true);
+ }
+ Object bundleEntry = BUNDLE_ENTRY_FIELD.get(con);
+ if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.FileBundleEntry"))
+ {
+ if (FILE_FIELD == null)
+ {
+ FILE_FIELD = bundleEntry.getClass().getDeclaredField("file");
+ FILE_FIELD.setAccessible(true);
+ }
+ File f = (File) FILE_FIELD.get(bundleEntry);
+ return f.getParentFile().getParentFile();
+ }
+ else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleEntry"))
+ {
+ url = bundle.getEntry("/");
+
+ con = url.openConnection();
+ con.setDefaultUseCaches(Resource.getDefaultUseCaches());
+
+ if (BUNDLE_ENTRY_FIELD == null)
+ {// this one will be a DirZipBundleEntry
+ BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry");
+ BUNDLE_ENTRY_FIELD.setAccessible(true);
+ }
+ bundleEntry = BUNDLE_ENTRY_FIELD.get(con);
+ if (BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY == null)
+ {
+ BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY = bundleEntry.getClass().getDeclaredField("bundleFile");
+ BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY.setAccessible(true);
+ }
+ Object zipBundleFile = BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY.get(bundleEntry);
+ if (ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE == null)
+ {
+ ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = zipBundleFile.getClass().getDeclaredField("zipFile");
+ ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.setAccessible(true);
+ }
+ ZipFile zipFile = (ZipFile) ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.get(zipBundleFile);
+ return new File(zipFile.getName());
+ }
+ else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.DirZipBundleEntry"))
+ {
+ // that will not happen as we did ask for the manifest not a
+ // directory.
+ }
+ }
+ else if ("bundle".equals(url.getProtocol()))
+ {
+ // observed this on felix-2.0.0
+ String location = bundle.getLocation();
+ if (location.startsWith("file:/"))
+ {
+ URI uri = new URI(URIUtil.encodePath(location));
+ return new File(uri);
+ }
+ else if (location.startsWith("file:"))
+ {
+ // location defined in the BundleArchive m_bundleArchive
+ // it is relative to relative to the BundleArchive's
+ // m_archiveRootDir
+ File res = new File(location.substring("file:".length()));
+ if (!res.exists()) { return null;
+ // Object bundleArchive = getFelixBundleArchive(bundle);
+ // File archiveRoot =
+ // getFelixBundleArchiveRootDir(bundleArchive);
+ // String currentLocation =
+ // getFelixBundleArchiveCurrentLocation(bundleArchive);
+ // System.err.println("Got the archive root " +
+ // archiveRoot.getAbsolutePath()
+ // + " current location " + currentLocation +
+ // " is directory ?");
+ // res = new File(archiveRoot, currentLocation != null
+ // ? currentLocation : location.substring("file:".length()));
+ }
+ return res;
+ }
+ else if (location.startsWith("reference:file:"))
+ {
+ location = URLDecoder.decode(location.substring("reference:".length()), "UTF-8");
+ File file = new File(location.substring("file:".length()));
+ return file;
+ }
+ else
+ {
+ //Resort to introspection on felix:
+ return getBundleInstallLocationInFelix(bundle);
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Locate a file inside a bundle.
+ *
+ * @param bundle
+ * @param path
+ * @return file object
+ * @throws Exception
+ */
+ public File getFileInBundle(Bundle bundle, String path) throws Exception
+ {
+ if (path != null && path.length() > 0 && path.charAt(0) == '/')
+ {
+ path = path.substring(1);
+ }
+ File bundleInstall = getBundleInstallLocation(bundle);
+ File webapp = path != null && path.length() != 0 ? new File(bundleInstall, path) : bundleInstall;
+ if (!webapp.exists()) { throw new IllegalArgumentException("Unable to locate " + path
+ + " inside "
+ + bundle.getSymbolicName()
+ + " ("
+ + (bundleInstall != null ? bundleInstall.getAbsolutePath() : " no_bundle_location ")
+ + ")"); }
+ return webapp;
+ }
+
+ /**
+ * Helper method equivalent to Bundle#getEntry(String entryPath) except that
+ * it searches for entries in the fragments by using the Bundle#findEntries
+ * method.
+ *
+ * @param bundle
+ * @param entryPath
+ * @return null or all the entries found for that path.
+ */
+ public Enumeration<URL> findEntries(Bundle bundle, String entryPath)
+ {
+ int last = entryPath.lastIndexOf('/');
+ String path = last != -1 && last < entryPath.length() - 2 ? entryPath.substring(0, last) : "/";
+ if (!path.startsWith("/"))
+ {
+ path = "/" + path;
+ }
+ String pattern = last != -1 && last < entryPath.length() - 2 ? entryPath.substring(last + 1) : entryPath;
+ Enumeration<URL> enUrls = bundle.findEntries(path, pattern, false);
+ return enUrls;
+ }
+
+ /**
+ * If the bundle is a jar, returns the jar. If the bundle is a folder, look
+ * inside it and search for jars that it returns.
+ * <p>
+ * Good enough for our purpose (TldLocationsCache when it scans for tld
+ * files inside jars alone. In fact we only support the second situation for
+ * development purpose where the bundle was imported in pde and the classes
+ * kept in a jar.
+ * </p>
+ *
+ * @param bundle
+ * @return The jar(s) file that is either the bundle itself, either the jars
+ * embedded inside it.
+ */
+ public File[] locateJarsInsideBundle(Bundle bundle) throws Exception
+ {
+ File jasperLocation = getBundleInstallLocation(bundle);
+ if (jasperLocation.isDirectory())
+ {
+ // try to find the jar files inside this folder
+ ArrayList<File> urls = new ArrayList<File>();
+ for (File f : jasperLocation.listFiles())
+ {
+ if (f.getName().endsWith(".jar") && f.isFile())
+ {
+ urls.add(f);
+ }
+ else if (f.isDirectory() && f.getName().equals("lib"))
+ {
+ for (File f2 : jasperLocation.listFiles())
+ {
+ if (f2.getName().endsWith(".jar") && f2.isFile())
+ {
+ urls.add(f2);
+ }
+ }
+ }
+ }
+ return urls.toArray(new File[urls.size()]);
+ }
+ else
+ {
+ return new File[] { jasperLocation };
+ }
+ }
+
+ // introspection on equinox to invoke the getLocalURL method on
+ // BundleURLConnection
+ // equivalent to using the FileLocator without depending on an equinox
+ // class.
+ private static Method BUNDLE_URL_CONNECTION_getLocalURL = null;
+
+ private static Method BUNDLE_URL_CONNECTION_getFileURL = null;
+
+ /**
+ * Only useful for equinox: on felix we get the file:// or jar:// url
+ * already. Other OSGi implementations have not been tested
+ * <p>
+ * Get a URL to the bundle entry that uses a common protocol (i.e. file:
+ * jar: or http: etc.).
+ * </p>
+ *
+ * @return a URL to the bundle entry that uses a common protocol
+ */
+ public static URL getLocalURL(URL url)
+ {
+ if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
+ {
+ try
+ {
+ URLConnection conn = url.openConnection();
+ conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
+ if (BUNDLE_URL_CONNECTION_getLocalURL == null && conn.getClass().getName().equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
+ {
+ BUNDLE_URL_CONNECTION_getLocalURL = conn.getClass().getMethod("getLocalURL", null);
+ BUNDLE_URL_CONNECTION_getLocalURL.setAccessible(true);
+ }
+ if (BUNDLE_URL_CONNECTION_getLocalURL != null) { return (URL) BUNDLE_URL_CONNECTION_getLocalURL.invoke(conn, null); }
+ }
+ catch (Throwable t)
+ {
+ System.err.println("Unable to locate the OSGi url: '" + url + "'.");
+ t.printStackTrace();
+ }
+ }
+ return url;
+ }
+
+ /**
+ * Only useful for equinox: on felix we get the file:// url already. Other
+ * OSGi implementations have not been tested
+ * <p>
+ * Get a URL to the content of the bundle entry that uses the file:
+ * protocol. The content of the bundle entry may be downloaded or extracted
+ * to the local file system in order to create a file: URL.
+ *
+ * @return a URL to the content of the bundle entry that uses the file:
+ * protocol
+ * </p>
+ */
+ public static URL getFileURL(URL url)
+ {
+ if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
+ {
+ try
+ {
+ URLConnection conn = url.openConnection();
+ conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
+ if (BUNDLE_URL_CONNECTION_getFileURL == null && conn.getClass().getName().equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
+ {
+ BUNDLE_URL_CONNECTION_getFileURL = conn.getClass().getMethod("getFileURL", null);
+ BUNDLE_URL_CONNECTION_getFileURL.setAccessible(true);
+ }
+ if (BUNDLE_URL_CONNECTION_getFileURL != null) { return (URL) BUNDLE_URL_CONNECTION_getFileURL.invoke(conn, null); }
+ }
+ catch (Throwable t)
+ {
+ t.printStackTrace();
+ }
+ }
+ return url;
+ }
+
+ // Felix introspection
+ private static Method Felix_BundleImpl_getArchive_method;
+ private static Method Felix_BundleArchive_getCurrentRevision_method;
+ private static Method Felix_BundleRevision_getRevisionRootDir_method;
+
+ private static boolean felixIntroSpectionDone = false;
+
+ /**
+ * Introspection of the implementation classes of Felix-3.x and Felix-4.x.
+ * <p>
+ * See org.apache.felix.framework.cache
+ * In pseudo code:
+ * <code>
+ * File revRootDir = BundleImpl.getArchive().getCurrentRevision().getRevisionRootDir();
+ * return new File(revRootDir, bundle.jar) if it exists?
+ * else return revRootDir
+ * </p>
+ * @param bundle
+ * @return The File or null if we failed to find it.
+ */
+ private static File getBundleInstallLocationInFelix(Bundle bundle)
+ {
+ if (Felix_BundleImpl_getArchive_method == null) {
+ if (felixIntroSpectionDone)
+ {
+ return null;
+ }
+ felixIntroSpectionDone = true;
+ try
+ {
+ Felix_BundleImpl_getArchive_method = bundle.getClass().getDeclaredMethod("getArchive", new Class[] {});
+ Felix_BundleImpl_getArchive_method.setAccessible(true);
+ Object archive = Felix_BundleImpl_getArchive_method.invoke(bundle);
+ Class bundleArchiveClass = archive.getClass();
+ Felix_BundleArchive_getCurrentRevision_method = bundleArchiveClass.getDeclaredMethod("getCurrentRevision", new Class[] {});
+ Felix_BundleArchive_getCurrentRevision_method.setAccessible(true);
+ Object revision = Felix_BundleArchive_getCurrentRevision_method.invoke(archive);
+ Class bundleRevisionClass = revision.getClass();
+ Felix_BundleRevision_getRevisionRootDir_method = bundleRevisionClass.getMethod("getRevisionRootDir", new Class[] {});
+ Felix_BundleRevision_getRevisionRootDir_method.setAccessible(true);
+ }
+ catch (Throwable t)
+ {
+ //nevermind?
+ //t.printStackTrace();
+ Felix_BundleImpl_getArchive_method = null;
+ return null;
+ }
+ }
+ if (Felix_BundleImpl_getArchive_method != null)
+ {
+ try
+ {
+ Object archive = Felix_BundleImpl_getArchive_method.invoke(bundle);
+ Object revision = Felix_BundleArchive_getCurrentRevision_method.invoke(archive);
+ File revRootDir = (File)Felix_BundleRevision_getRevisionRootDir_method.invoke(revision);
+ //System.err.println("Got the archive revision root dir " + revRootDir.getAbsolutePath());
+ File bundleJar = new File(revRootDir, "bundle.jar");
+ if (bundleJar.exists())
+ {
+ //bundle.jar is hardcoded in org.apache.felix.framework.cache.JarRevision
+ //when it is not a bundle.jar, then the bundle location starts with 'file:' and we have already
+ //taken care if that scheme earlier.
+ return bundleJar;
+ }
+ else //sanity check?: if (new File(revRootDir, "META-INF/MANIFEST.MF").exists())
+ {
+ //this is a DirectoryRevision
+ return revRootDir;
+ }
+ }
+ catch (Throwable t)
+ {
+ //best effort: nevermind
+ //t.printStackTrace();
+ }
+ }
+ return null;
+ }
+// -- end Felix introspection
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java
new file mode 100644
index 0000000000..384b392c9a
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/old-org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java
@@ -0,0 +1,383 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.utils.internal;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
+
+/**
+ * When the PackageAdmin service is activated we can look for the fragments
+ * attached to this bundle and "activate" them.
+ */
+public class PackageAdminServiceTracker implements ServiceListener
+{
+ private BundleContext _context;
+
+ private List<BundleActivator> _activatedFragments = new ArrayList<BundleActivator>();
+
+ private boolean _fragmentsWereActivated = false;
+
+ // Use the deprecated StartLevel to stay compatible with older versions of
+ // OSGi.
+ private StartLevel _startLevel;
+
+ private int _maxStartLevel = 6;
+
+ public static PackageAdminServiceTracker INSTANCE = null;
+
+ public PackageAdminServiceTracker(BundleContext context)
+ {
+ INSTANCE = this;
+ _context = context;
+ if (!setup())
+ {
+ try
+ {
+ _context.addServiceListener(this, "(objectclass=" + PackageAdmin.class.getName() + ")");
+ }
+ catch (InvalidSyntaxException e)
+ {
+ e.printStackTrace(); // won't happen
+ }
+ }
+ }
+
+ /**
+ * @return true if the fragments were activated by this method.
+ */
+ private boolean setup()
+ {
+ ServiceReference sr = _context.getServiceReference(PackageAdmin.class.getName());
+ _fragmentsWereActivated = sr != null;
+ if (sr != null) invokeFragmentActivators(sr);
+
+ sr = _context.getServiceReference(StartLevel.class.getName());
+ if (sr != null)
+ {
+ _startLevel = (StartLevel) _context.getService(sr);
+ try
+ {
+ _maxStartLevel = Integer.parseInt(System.getProperty("osgi.startLevel", "6"));
+ }
+ catch (Exception e)
+ {
+ // nevermind default on the usual.
+ _maxStartLevel = 6;
+ }
+ }
+ return _fragmentsWereActivated;
+ }
+
+ /**
+ * Invokes the optional BundleActivator in each fragment. By convention the
+ * bundle activator for a fragment must be in the package that is defined by
+ * the symbolic name of the fragment and the name of the class must be
+ * 'FragmentActivator'.
+ *
+ * @param event The <code>ServiceEvent</code> object.
+ */
+ public void serviceChanged(ServiceEvent event)
+ {
+ if (event.getType() == ServiceEvent.REGISTERED)
+ {
+ invokeFragmentActivators(event.getServiceReference());
+ }
+ }
+
+ /**
+ * Helper to access the PackageAdmin and return the fragments hosted by a
+ * bundle. when we drop the support for the older versions of OSGi, we will
+ * stop using the PackageAdmin service.
+ *
+ * @param bundle
+ * @return
+ */
+ public Bundle[] getFragments(Bundle bundle)
+ {
+ ServiceReference sr = _context.getServiceReference(PackageAdmin.class.getName());
+ if (sr == null)
+ {// we should never be here really.
+ return null;
+ }
+ PackageAdmin admin = (PackageAdmin) _context.getService(sr);
+ return admin.getFragments(bundle);
+ }
+
+ /**
+ * Returns the fragments and the required-bundles of a bundle. Recursively
+ * collect the required-bundles and fragment when the directive
+ * visibility:=reexport is added to a required-bundle.
+ *
+ * @param bundle
+ * @param webFragOrAnnotationOrResources
+ * @return
+ */
+ public Bundle[] getFragmentsAndRequiredBundles(Bundle bundle)
+ {
+ ServiceReference sr = _context.getServiceReference(PackageAdmin.class.getName());
+ if (sr == null)
+ {// we should never be here really.
+ return null;
+ }
+ PackageAdmin admin = (PackageAdmin) _context.getService(sr);
+ LinkedHashMap<String, Bundle> deps = new LinkedHashMap<String, Bundle>();
+ collectFragmentsAndRequiredBundles(bundle, admin, deps, false);
+ return deps.values().toArray(new Bundle[deps.size()]);
+ }
+
+ /**
+ * Returns the fragments and the required-bundles. Collects them
+ * transitively when the directive 'visibility:=reexport' is added to a
+ * required-bundle.
+ *
+ * @param bundle
+ * @param webFragOrAnnotationOrResources
+ * @return
+ */
+ protected void collectFragmentsAndRequiredBundles(Bundle bundle, PackageAdmin admin, Map<String, Bundle> deps, boolean onlyReexport)
+ {
+ Bundle[] fragments = admin.getFragments(bundle);
+ if (fragments != null)
+ {
+ // Also add the bundles required by the fragments.
+ // this way we can inject onto an existing web-bundle a set of
+ // bundles that extend it
+ for (Bundle f : fragments)
+ {
+ if (!deps.keySet().contains(f.getSymbolicName()))
+ {
+ deps.put(f.getSymbolicName(), f);
+ collectRequiredBundles(f, admin, deps, onlyReexport);
+ }
+ }
+ }
+ collectRequiredBundles(bundle, admin, deps, onlyReexport);
+ }
+
+ /**
+ * A simplistic but good enough parser for the Require-Bundle header. Parses
+ * the version range attribute and the visibility directive.
+ *
+ * @param onlyReexport true to collect resources and web-fragments
+ * transitively if and only if the directive visibility is
+ * reexport.
+ * @param bundle
+ * @return The map of required bundles associated to the value of the
+ * jetty-web attribute.
+ */
+ protected void collectRequiredBundles(Bundle bundle, PackageAdmin admin, Map<String, Bundle> deps, boolean onlyReexport)
+ {
+ String requiredBundleHeader = (String) bundle.getHeaders().get("Require-Bundle");
+ if (requiredBundleHeader == null) { return; }
+ StringTokenizer tokenizer = new ManifestTokenizer(requiredBundleHeader);
+ while (tokenizer.hasMoreTokens())
+ {
+ String tok = tokenizer.nextToken().trim();
+ StringTokenizer tokenizer2 = new StringTokenizer(tok, ";");
+ String symbolicName = tokenizer2.nextToken().trim();
+ if (deps.keySet().contains(symbolicName))
+ {
+ // was already added. 2 dependencies pointing at the same
+ // bundle.
+ continue;
+ }
+ String versionRange = null;
+ boolean reexport = false;
+ while (tokenizer2.hasMoreTokens())
+ {
+ String next = tokenizer2.nextToken().trim();
+ if (next.startsWith("bundle-version="))
+ {
+ if (next.startsWith("bundle-version=\"") || next.startsWith("bundle-version='"))
+ {
+ versionRange = next.substring("bundle-version=\"".length(), next.length() - 1);
+ }
+ else
+ {
+ versionRange = next.substring("bundle-version=".length());
+ }
+ }
+ else if (next.equals("visibility:=reexport"))
+ {
+ reexport = true;
+ }
+ }
+ if (!reexport && onlyReexport) { return; }
+ Bundle[] reqBundles = admin.getBundles(symbolicName, versionRange);
+ if (reqBundles != null && reqBundles.length != 0)
+ {
+ Bundle reqBundle = null;
+ for (Bundle b : reqBundles)
+ {
+ if (b.getState() == Bundle.ACTIVE || b.getState() == Bundle.STARTING)
+ {
+ reqBundle = b;
+ break;
+ }
+ }
+ if (reqBundle == null)
+ {
+ // strange? in OSGi with Require-Bundle,
+ // the dependent bundle is supposed to be active already
+ reqBundle = reqBundles[0];
+ }
+ deps.put(reqBundle.getSymbolicName(), reqBundle);
+ collectFragmentsAndRequiredBundles(reqBundle, admin, deps, true);
+ }
+ }
+ }
+
+ private void invokeFragmentActivators(ServiceReference sr)
+ {
+ PackageAdmin admin = (PackageAdmin) _context.getService(sr);
+ Bundle[] fragments = admin.getFragments(_context.getBundle());
+ if (fragments == null) { return; }
+ for (Bundle frag : fragments)
+ {
+ // find a convention to look for a class inside the fragment.
+ try
+ {
+ String fragmentActivator = frag.getSymbolicName() + ".FragmentActivator";
+ Class<?> c = Class.forName(fragmentActivator);
+ if (c != null)
+ {
+ BundleActivator bActivator = (BundleActivator) c.newInstance();
+ bActivator.start(_context);
+ _activatedFragments.add(bActivator);
+ }
+ }
+ catch (NullPointerException e)
+ {
+ // e.printStackTrace();
+ }
+ catch (InstantiationException e)
+ {
+ // e.printStackTrace();
+ }
+ catch (IllegalAccessException e)
+ {
+ // e.printStackTrace();
+ }
+ catch (ClassNotFoundException e)
+ {
+ // e.printStackTrace();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void stop()
+ {
+ INSTANCE = null;
+ for (BundleActivator fragAct : _activatedFragments)
+ {
+ try
+ {
+ fragAct.stop(_context);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * @return true if the framework has completed all the start levels.
+ */
+ public boolean frameworkHasCompletedAutostarts()
+ {
+ return _startLevel == null ? true : _startLevel.getStartLevel() >= _maxStartLevel;
+ }
+
+ private static class ManifestTokenizer extends StringTokenizer
+ {
+
+ public ManifestTokenizer(String header)
+ {
+ super(header, ",");
+ }
+
+ @Override
+ public String nextToken()
+ {
+ String token = super.nextToken();
+
+ while (hasOpenQuote(token) && hasMoreTokens())
+ {
+ token += "," + super.nextToken();
+ }
+ return token;
+ }
+
+ private boolean hasOpenQuote(String token)
+ {
+ int i = -1;
+ do
+ {
+ int quote = getQuote(token, i + 1);
+ if (quote < 0) { return false; }
+
+ i = token.indexOf(quote, i + 1);
+ i = token.indexOf(quote, i + 1);
+ }
+ while (i >= 0);
+ return true;
+ }
+
+ private int getQuote(String token, int offset)
+ {
+ int i = token.indexOf('"', offset);
+ int j = token.indexOf('\'', offset);
+ if (i < 0)
+ {
+ if (j < 0)
+ {
+ return -1;
+ }
+ else
+ {
+ return '\'';
+ }
+ }
+ if (j < 0) { return '"'; }
+ if (i < j) { return '"'; }
+ return '\'';
+ }
+
+ }
+
+}
+
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
index 98ac22b6eb..b4ae909819 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
@@ -18,12 +18,16 @@
package org.eclipse.jetty.osgi.annotations;
+import java.util.ArrayList;
+import java.util.List;
+
import org.eclipse.jetty.annotations.AbstractDiscoverableAnnotationHandler;
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
import org.eclipse.jetty.annotations.ClassNameResolver;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractContextProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractContextProvider.java
new file mode 100644
index 0000000000..6f07480ab6
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractContextProvider.java
@@ -0,0 +1,360 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.AppProvider;
+import org.eclipse.jetty.deploy.DeploymentManager;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
+import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
+import org.eclipse.jetty.osgi.boot.utils.EventSender;
+import org.eclipse.jetty.osgi.boot.utils.OSGiClassLoader;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.JarResource;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.xml.XmlConfiguration;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceRegistration;
+
+
+
+
+/**
+ * AbstractContextProvider
+ *
+ *
+ */
+public abstract class AbstractContextProvider extends AbstractLifeCycle implements AppProvider
+{
+ private static final Logger LOG = Log.getLogger(AbstractContextProvider.class);
+
+ private DeploymentManager _deploymentManager;
+
+
+ private ServerInstanceWrapper _serverWrapper;
+
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * BundleApp
+ *
+ *
+ */
+ public class OSGiApp extends AbstractOSGiApp
+ {
+ private String _contextFile;
+ private ContextHandler _contextHandler;
+ private boolean _configured = false;
+
+ public OSGiApp(DeploymentManager manager, AppProvider provider, String originId, Bundle bundle, String contextFile)
+ {
+ super(manager, provider, bundle, originId);
+ _contextFile = contextFile;
+ }
+
+ public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String contextFile, String originId)
+ {
+ super(manager, provider, bundle, properties, originId);
+ _contextFile = contextFile;
+ }
+
+ public String getContextFile ()
+ {
+ return _contextFile;
+ }
+
+ public void setHandler(ContextHandler h)
+ {
+ _contextHandler = h;
+ }
+
+ public ContextHandler createContextHandler()
+ throws Exception
+ {
+ configureContextHandler();
+ return _contextHandler;
+ }
+
+ public void configureContextHandler()
+ throws Exception
+ {
+ if (_configured)
+ return;
+
+ _configured = true;
+
+ //Override for bundle root may have been set
+ String bundleOverrideLocation = (String)_properties.get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
+ if (bundleOverrideLocation == null)
+ bundleOverrideLocation = (String)_properties.get(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE);
+
+ //Location on filesystem of bundle or the bundle override location
+ File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(_bundle);
+ File root = (bundleOverrideLocation==null?bundleLocation:new File(bundleOverrideLocation));
+ Resource rootResource = Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(root.toURI().toURL()));
+
+ //try and make sure the rootResource is useable - if its a jar then make it a jar file url
+ if (rootResource.exists()&& !rootResource.isDirectory() && !rootResource.toString().startsWith("jar:"))
+ {
+ Resource jarResource = JarResource.newJarResource(rootResource);
+ if (jarResource.exists() && jarResource.isDirectory())
+ rootResource = jarResource;
+ }
+
+ //Set the base resource of the ContextHandler, if not already set, can also be overridden by the context xml file
+ if (_contextHandler != null && _contextHandler.getBaseResource() == null)
+ {
+ _contextHandler.setBaseResource(rootResource);
+ }
+
+ //Use a classloader that knows about the common jetty parent loader, and also the bundle
+ OSGiClassLoader classLoader = new OSGiClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps(), _bundle);
+
+ //if there is a context file, find it and apply it
+ if (_contextFile == null && _contextHandler == null)
+ throw new IllegalStateException("No context file or ContextHandler");
+
+ if (_contextFile != null)
+ {
+ //apply the contextFile, creating the ContextHandler, the DeploymentManager will register it in the ContextHandlerCollection
+ Resource res = null;
+
+ //try to find the context file in the filesystem
+ if (_contextFile.startsWith("/"))
+ res = getFileAsResource(_contextFile);
+
+ //try to find it relative to jetty home
+ if (res == null)
+ {
+ //See if the specific server we are related to has jetty.home set
+ String jettyHome = (String)getServerInstanceWrapper().getServer().getAttribute(OSGiServerConstants.JETTY_HOME);
+ if (jettyHome != null)
+ res = getFileAsResource(jettyHome, _contextFile);
+
+ //try to see if a SystemProperty for jetty.home is set
+ if (res == null)
+ {
+ jettyHome = System.getProperty(OSGiServerConstants.JETTY_HOME);
+
+ if (jettyHome != null)
+ {
+ if (jettyHome.startsWith("\"") || jettyHome.startsWith("'"))
+ jettyHome = jettyHome.substring(1);
+ if (jettyHome.endsWith("\"") || (jettyHome.endsWith("'")))
+ jettyHome = jettyHome.substring(0,jettyHome.length()-1);
+
+ res = getFileAsResource(jettyHome, _contextFile);
+ if (LOG.isDebugEnabled()) LOG.debug("jetty home context file:"+res);
+ }
+ }
+ }
+
+ //try to find it relative to an override location that has been specified
+ if (res == null)
+ {
+ if (bundleOverrideLocation != null)
+ {
+ res = getFileAsResource(Resource.newResource(bundleOverrideLocation).getFile(), _contextFile);
+ if (LOG.isDebugEnabled()) LOG.debug("Bundle override location context file:"+res);
+ }
+ }
+
+ //try to find it relative to the bundle in which it is being deployed
+ if (res == null)
+ {
+ if (_contextFile.startsWith("./"))
+ _contextFile = _contextFile.substring(1);
+
+ if (!_contextFile.startsWith("/"))
+ _contextFile = "/" + _contextFile;
+
+ URL contextURL = _bundle.getEntry(_contextFile);
+ if (contextURL != null)
+ res = Resource.newResource(contextURL);
+ }
+
+ //apply the context xml file, either to an existing ContextHandler, or letting the
+ //it create the ContextHandler as necessary
+ if (res != null)
+ {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
+ LOG.debug("Context classloader = " + cl);
+ try
+ {
+ Thread.currentThread().setContextClassLoader(classLoader);
+
+ XmlConfiguration xmlConfiguration = new XmlConfiguration(res.getInputStream());
+ HashMap properties = new HashMap();
+ //put the server instance in
+ properties.put("Server", getServerInstanceWrapper().getServer());
+ //put in the location of the bundle root
+ properties.put("bundle.root", rootResource.toString());
+
+ // insert the bundle's location as a property.
+ xmlConfiguration.getProperties().putAll(properties);
+
+ if (_contextHandler == null)
+ _contextHandler = (ContextHandler) xmlConfiguration.configure();
+ else
+ xmlConfiguration.configure(_contextHandler);
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
+ }
+
+ //Set up the class loader we created
+ _contextHandler.setClassLoader(classLoader);
+
+
+ //If a bundle/service property specifies context path, let it override the context xml
+ String contextPath = (String)_properties.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
+ if (contextPath == null)
+ contextPath = (String)_properties.get(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH);
+ if (contextPath != null)
+ _contextHandler.setContextPath(contextPath);
+
+ //osgi Enterprise Spec r4 p.427
+ _contextHandler.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, _bundle.getBundleContext());
+
+ //make sure we protect also the osgi dirs specified by OSGi Enterprise spec
+ String[] targets = _contextHandler.getProtectedTargets();
+ int length = (targets==null?0:targets.length);
+
+ String[] updatedTargets = null;
+ if (targets != null)
+ {
+ updatedTargets = new String[length+OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
+ System.arraycopy(targets, 0, updatedTargets, 0, length);
+
+ }
+ else
+ updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
+ System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length);
+ _contextHandler.setProtectedTargets(updatedTargets);
+
+ }
+
+
+ private Resource getFileAsResource (String dir, String file)
+ {
+ Resource r = null;
+ try
+ {
+ File asFile = new File (dir, file);
+ if (asFile.exists())
+ r = Resource.newResource(asFile);
+ }
+ catch (Exception e)
+ {
+ r = null;
+ }
+ return r;
+ }
+
+ private Resource getFileAsResource (String file)
+ {
+ Resource r = null;
+ try
+ {
+ File asFile = new File (file);
+ if (asFile.exists())
+ r = Resource.newResource(asFile);
+ }
+ catch (Exception e)
+ {
+ r = null;
+ }
+ return r;
+ }
+
+ private Resource getFileAsResource (File dir, String file)
+ {
+ Resource r = null;
+ try
+ {
+ File asFile = new File (dir, file);
+ if (asFile.exists())
+ r = Resource.newResource(asFile);
+ }
+ catch (Exception e)
+ {
+ r = null;
+ }
+ return r;
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ public AbstractContextProvider(ServerInstanceWrapper wrapper)
+ {
+ _serverWrapper = wrapper;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public ServerInstanceWrapper getServerInstanceWrapper()
+ {
+ return _serverWrapper;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.deploy.AppProvider#createContextHandler(org.eclipse.jetty.deploy.App)
+ */
+ public ContextHandler createContextHandler(App app) throws Exception
+ {
+ if (app == null)
+ return null;
+ if (!(app instanceof OSGiApp))
+ throw new IllegalStateException(app+" is not a BundleApp");
+
+ //Create a ContextHandler suitable to deploy in OSGi
+ ContextHandler h = ((OSGiApp)app).createContextHandler();
+ return h;
+ }
+
+ /* ------------------------------------------------------------ */
+ public void setDeploymentManager(DeploymentManager deploymentManager)
+ {
+ _deploymentManager = deploymentManager;
+ }
+
+ /* ------------------------------------------------------------ */
+ public DeploymentManager getDeploymentManager()
+ {
+ return _deploymentManager;
+ }
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractOSGiApp.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractOSGiApp.java
new file mode 100644
index 0000000000..be09d2cfd6
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractOSGiApp.java
@@ -0,0 +1,120 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.AppProvider;
+import org.eclipse.jetty.deploy.DeploymentManager;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceRegistration;
+
+
+
+/**
+ * AbstractBundleApp
+ *
+ *
+ */
+public abstract class AbstractOSGiApp extends App
+{
+ protected Bundle _bundle;
+ protected Dictionary _properties;
+ protected ServiceRegistration _registration;
+
+ /* ------------------------------------------------------------ */
+ public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
+ {
+ super(manager, provider, originId);
+ _properties = bundle.getHeaders();
+ _bundle = bundle;
+ }
+ /* ------------------------------------------------------------ */
+ public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId)
+ {
+ super(manager, provider, originId);
+ _properties = properties;
+ _bundle = bundle;
+ }
+
+ /* ------------------------------------------------------------ */
+ public String getBundleSymbolicName()
+ {
+ return _bundle.getSymbolicName();
+ }
+
+ /* ------------------------------------------------------------ */
+ public String getBundleVersionAsString()
+ {
+ if (_bundle.getVersion() == null)
+ return null;
+ return _bundle.getVersion().toString();
+ }
+
+ /* ------------------------------------------------------------ */
+ public Bundle getBundle()
+ {
+ return _bundle;
+ }
+
+ /* ------------------------------------------------------------ */
+ public void setRegistration (ServiceRegistration registration)
+ {
+ _registration = registration;
+ }
+
+ /* ------------------------------------------------------------ */
+ public ServiceRegistration getRegistration ()
+ {
+ return _registration;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public void registerAsOSGiService() throws Exception
+ {
+ if (_registration == null)
+ {
+ Dictionary<String,String> properties = new Hashtable<String,String>();
+ properties.put(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK);
+ if (getBundleSymbolicName() != null)
+ properties.put(OSGiWebappConstants.OSGI_WEB_SYMBOLICNAME, getBundleSymbolicName());
+ if (getBundleVersionAsString() != null)
+ properties.put(OSGiWebappConstants.OSGI_WEB_VERSION, getBundleVersionAsString());
+ properties.put(OSGiWebappConstants.OSGI_WEB_CONTEXTPATH, getContextPath());
+ ServiceRegistration rego = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ContextHandler.class.getName(), getContextHandler(), properties);
+ setRegistration(rego);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ protected void deregisterAsOSGiService() throws Exception
+ {
+ if (_registration == null)
+ return;
+
+ _registration.unregister();
+ _registration = null;
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java
new file mode 100644
index 0000000000..73da20f41c
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/AbstractWebAppProvider.java
@@ -0,0 +1,552 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.AppProvider;
+import org.eclipse.jetty.deploy.DeploymentManager;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
+import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
+import org.eclipse.jetty.osgi.boot.internal.webapp.OSGiWebappClassLoader;
+import org.eclipse.jetty.osgi.boot.utils.EventSender;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.xml.XmlConfiguration;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+
+
+
+/**
+ * AbstractWebAppProvider
+ *
+ *
+ */
+public abstract class AbstractWebAppProvider extends AbstractLifeCycle implements AppProvider
+{
+ private static final Logger LOG = Log.getLogger(AbstractWebAppProvider.class);
+
+ public static String __defaultConfigurations[] = {
+ "org.eclipse.jetty.osgi.boot.OSGiWebInfConfiguration",
+ "org.eclipse.jetty.webapp.WebXmlConfiguration",
+ "org.eclipse.jetty.osgi.boot.OSGiMetaInfConfiguration",
+ "org.eclipse.jetty.webapp.FragmentConfiguration",
+ "org.eclipse.jetty.webapp.JettyWebXmlConfiguration"//,
+ //"org.eclipse.jetty.osgi.boot.jsp.TagLibOSGiConfiguration"
+ };
+
+ public static void setDefaultConfigurations (String[] defaultConfigs)
+ {
+ __defaultConfigurations = defaultConfigs;
+ }
+
+ public static String[] getDefaultConfigurations ()
+ {
+ return __defaultConfigurations;
+ }
+
+
+ private boolean _parentLoaderPriority;
+
+ private String _defaultsDescriptor;
+
+ private boolean _extractWars = true; //See WebAppContext.extractWars
+
+ private String _tldBundles;
+
+ private DeploymentManager _deploymentManager;
+
+ private String[] _configurationClasses;
+
+ private ServerInstanceWrapper _serverWrapper;
+
+ /* ------------------------------------------------------------ */
+ /**
+ * OSGiApp
+ *
+ *
+ */
+ public class OSGiApp extends AbstractOSGiApp
+ {
+ private String _contextPath;
+ private String _webAppPath;
+ private WebAppContext _webApp;
+
+ public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
+ {
+ super(manager, provider, bundle, originId);
+ }
+
+ public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId)
+ {
+ super(manager, provider, bundle, properties, originId);
+ }
+
+ public void setWebAppContext (WebAppContext webApp)
+ {
+ _webApp = webApp;
+ }
+
+ public String getContextPath()
+ {
+ return _contextPath;
+ }
+
+ public void setContextPath(String contextPath)
+ {
+ this._contextPath = contextPath;
+ }
+
+ public String getBundlePath()
+ {
+ return _webAppPath;
+ }
+
+ public void setWebAppPath(String path)
+ {
+ this._webAppPath = path;
+ }
+
+
+ public ContextHandler createContextHandler()
+ throws Exception
+ {
+ if (_webApp != null)
+ {
+ configureWebApp();
+ return _webApp;
+ }
+
+ createWebApp();
+ return _webApp;
+ }
+
+
+
+ protected void createWebApp ()
+ throws Exception
+ {
+ _webApp = newWebApp();
+ configureWebApp();
+ }
+
+ protected WebAppContext newWebApp ()
+ {
+ WebAppContext webApp = new WebAppContext();
+ webApp.setAttribute(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK);
+
+ //make sure we protect also the osgi dirs specified by OSGi Enterprise spec
+ String[] targets = webApp.getProtectedTargets();
+ String[] updatedTargets = null;
+ if (targets != null)
+ {
+ updatedTargets = new String[targets.length+OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
+ System.arraycopy(targets, 0, updatedTargets, 0, targets.length);
+ }
+ else
+ updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
+ System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length);
+ webApp.setProtectedTargets(updatedTargets);
+
+ return webApp;
+ }
+
+
+ public void configureWebApp()
+ throws Exception
+ {
+ //TODO turn this around and let any context.xml file get applied first, and have the properties override
+ _webApp.setContextPath(_contextPath);
+
+ //osgi Enterprise Spec r4 p.427
+ _webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, _bundle.getBundleContext());
+
+ String overrideBundleInstallLocation = (String)_properties.get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
+ File bundleInstallLocation =
+ (overrideBundleInstallLocation == null
+ ? BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(_bundle)
+ : new File(overrideBundleInstallLocation));
+ URL url = null;
+
+ //if the path wasn't set or it was ., then it is the root of the bundle's installed location
+ if (_webAppPath == null || _webAppPath.length() == 0 || ".".equals(_webAppPath))
+ {
+ url = bundleInstallLocation.toURI().toURL();
+ }
+ else
+ {
+ //Get the location of the root of the webapp inside the installed bundle
+ if (_webAppPath.startsWith("/") || _webAppPath.startsWith("file:"))
+ {
+ url = new File(_webAppPath).toURI().toURL();
+ }
+ else if (bundleInstallLocation != null && bundleInstallLocation.isDirectory())
+ {
+ url = new File(bundleInstallLocation, _webAppPath).toURI().toURL();
+ }
+ else if (bundleInstallLocation != null)
+ {
+ Enumeration<URL> urls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(_bundle, _webAppPath);
+ if (urls != null && urls.hasMoreElements())
+ url = urls.nextElement();
+ }
+ }
+
+ if (url == null)
+ {
+ throw new IllegalArgumentException("Unable to locate " + _webAppPath
+ + " in "
+ + (bundleInstallLocation != null ? bundleInstallLocation.getAbsolutePath() : "unlocated bundle '" + _bundle.getSymbolicName()+ "'"));
+ }
+
+ // converts bundleentry: protocol if necessary
+ _webApp.setWar(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(url).toString());
+
+ // Set up what has been configured on the provider
+ _webApp.setParentLoaderPriority(isParentLoaderPriority());
+ _webApp.setExtractWAR(isExtract());
+ if (getConfigurationClasses() != null)
+ _webApp.setConfigurationClasses(getConfigurationClasses());
+ else
+ _webApp.setConfigurationClasses(__defaultConfigurations);
+
+ if (getDefaultsDescriptor() != null)
+ _webApp.setDefaultsDescriptor(getDefaultsDescriptor());
+
+ //Set up configuration from manifest headers
+ //extra classpath
+ String tmp = (String)_properties.get(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH);
+ if (tmp != null)
+ _webApp.setExtraClasspath(tmp);
+
+ //web.xml
+ tmp = (String)_properties.get(OSGiWebappConstants.JETTY_WEB_XML_PATH);
+ if (tmp != null && tmp.trim().length() != 0)
+ {
+ File webXml = getFile (tmp, bundleInstallLocation);
+ if (webXml != null && webXml.exists())
+ _webApp.setDescriptor(webXml.getAbsolutePath());
+ }
+
+ //webdefault.xml
+ tmp = (String)_properties.get(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH);
+ if (tmp != null)
+ {
+ File defaultWebXml = getFile (tmp, bundleInstallLocation);
+ if (defaultWebXml != null && defaultWebXml.exists())
+ _webApp.setDefaultsDescriptor(defaultWebXml.getAbsolutePath());
+ }
+
+ //Handle Require-TldBundle
+ //This is a comma separated list of names of bundles that contain tlds that this webapp uses.
+ //We add them to the webapp classloader.
+ String requireTldBundles = (String)_properties.get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
+ String pathsToTldBundles = getPathsToRequiredBundles(requireTldBundles);
+
+
+ // make sure we provide access to all the jetty bundles by going
+ // through this bundle.
+ OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(_serverWrapper.getParentClassLoaderForWebapps(), _webApp, _bundle);
+
+ if (pathsToTldBundles != null)
+ webAppLoader.addClassPath(pathsToTldBundles);
+ _webApp.setClassLoader(webAppLoader);
+
+
+ // apply any META-INF/context.xml file that is found to configure
+ // the webapp first
+ applyMetaInfContextXml();
+
+ // pass the value of the require tld bundle so that the TagLibOSGiConfiguration
+ // can pick it up.
+ _webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles);
+
+ //Set up some attributes
+ // rfc66
+ _webApp.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, _bundle.getBundleContext());
+
+ // spring-dm-1.2.1 looks for the BundleContext as a different attribute.
+ // not a spec... but if we want to support
+ // org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
+ // then we need to do this to:
+ _webApp.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), _bundle.getBundleContext());
+
+ // also pass the bundle directly. sometimes a bundle does not have a
+ // bundlecontext.
+ // it is still useful to have access to the Bundle from the servlet
+ // context.
+ _webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, _bundle);
+ }
+
+ protected String getPathsToRequiredBundles (String requireTldBundles)
+ throws Exception
+ {
+ if (requireTldBundles == null) return null;
+
+ ServiceReference ref = _bundle.getBundleContext().getServiceReference(org.osgi.service.packageadmin.PackageAdmin.class.getName());
+ PackageAdmin packageAdmin = (ref == null) ? null : (PackageAdmin)_bundle.getBundleContext().getService(ref);
+ if (packageAdmin == null)
+ throw new IllegalStateException("Unable to get PackageAdmin reference to locate required Tld bundles");
+
+ StringBuilder paths = new StringBuilder();
+ String[] symbNames = requireTldBundles.split(", ");
+
+ for (String symbName : symbNames)
+ {
+ Bundle[] bs = packageAdmin.getBundles(symbName, null);
+ if (bs == null || bs.length == 0)
+ {
+ throw new IllegalArgumentException("Unable to locate the bundle '" + symbName
+ + "' specified by "
+ + OSGiWebappConstants.REQUIRE_TLD_BUNDLE
+ + " in manifest of "
+ + (_bundle == null ? "unknown" : _bundle.getSymbolicName()));
+ }
+
+ File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bs[0]);
+ if (paths.length() > 0) paths.append(", ");
+ paths.append(f.toURI().toURL().toString());
+ LOG.debug("getPathsToRequiredBundles: bundle path=" + bs[0].getLocation() + " uri=" + f.toURI());
+ }
+
+ return paths.toString();
+ }
+
+
+ protected void applyMetaInfContextXml()
+ throws Exception
+ {
+ if (_bundle == null) return;
+ if (_webApp == null) return;
+
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ LOG.debug("Context classloader = " + cl);
+ try
+ {
+
+ Thread.currentThread().setContextClassLoader(_webApp.getClassLoader());
+
+ //TODO replace this with getting the InputStream so we don't cache in URL
+ // find if there is a META-INF/context.xml file
+ URL contextXmlUrl = _bundle.getEntry("/META-INF/jetty-webapp-context.xml");
+ if (contextXmlUrl == null) return;
+
+ // Apply it just as the standard jetty ContextProvider would do
+ LOG.info("Applying " + contextXmlUrl + " to " + _webApp);
+
+ XmlConfiguration xmlConfiguration = new XmlConfiguration(contextXmlUrl);
+ HashMap properties = new HashMap();
+ properties.put("Server", getDeploymentManager().getServer());
+ xmlConfiguration.getProperties().putAll(properties);
+ xmlConfiguration.configure(_webApp);
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
+
+ private File getFile (String file, File bundleInstall)
+ {
+ if (file == null)
+ return null;
+
+ if (file.startsWith("/") || file.startsWith("file:/"))
+ return new File(file);
+ else
+ return new File(bundleInstall, file);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ public AbstractWebAppProvider (ServerInstanceWrapper wrapper)
+ {
+ _serverWrapper = wrapper;
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Get the parentLoaderPriority.
+ *
+ * @return the parentLoaderPriority
+ */
+ public boolean isParentLoaderPriority()
+ {
+ return _parentLoaderPriority;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Set the parentLoaderPriority.
+ *
+ * @param parentLoaderPriority the parentLoaderPriority to set
+ */
+ public void setParentLoaderPriority(boolean parentLoaderPriority)
+ {
+ _parentLoaderPriority = parentLoaderPriority;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Get the defaultsDescriptor.
+ *
+ * @return the defaultsDescriptor
+ */
+ public String getDefaultsDescriptor()
+ {
+ return _defaultsDescriptor;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Set the defaultsDescriptor.
+ *
+ * @param defaultsDescriptor the defaultsDescriptor to set
+ */
+ public void setDefaultsDescriptor(String defaultsDescriptor)
+ {
+ _defaultsDescriptor = defaultsDescriptor;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public boolean isExtract()
+ {
+ return _extractWars;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public void setExtract(boolean extract)
+ {
+ _extractWars = extract;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param tldBundles Comma separated list of bundles that contain tld jars
+ * that should be setup on the jetty instances created here.
+ */
+ public void setTldBundles(String tldBundles)
+ {
+ _tldBundles = tldBundles;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return The list of bundles that contain tld jars that should be setup on
+ * the jetty instances created here.
+ */
+ public String getTldBundles()
+ {
+ return _tldBundles;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param configurations The configuration class names.
+ */
+ public void setConfigurationClasses(String[] configurations)
+ {
+ _configurationClasses = configurations == null ? null : (String[]) configurations.clone();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ *
+ */
+ public String[] getConfigurationClasses()
+ {
+ return _configurationClasses;
+ }
+
+ /* ------------------------------------------------------------ */
+ public void setServerInstanceWrapper(ServerInstanceWrapper wrapper)
+ {
+ _serverWrapper = wrapper;
+ }
+
+ public ServerInstanceWrapper getServerInstanceWrapper()
+ {
+ return _serverWrapper;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return
+ */
+ public DeploymentManager getDeploymentManager()
+ {
+ return _deploymentManager;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.deploy.AppProvider#setDeploymentManager(org.eclipse.jetty.deploy.DeploymentManager)
+ */
+ public void setDeploymentManager(DeploymentManager deploymentManager)
+ {
+ _deploymentManager = deploymentManager;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public ContextHandler createContextHandler(App app) throws Exception
+ {
+ if (app == null)
+ return null;
+ if (!(app instanceof OSGiApp))
+ throw new IllegalStateException(app+" is not a BundleApp");
+
+ //Create a WebAppContext suitable to deploy in OSGi
+ ContextHandler ch = ((OSGiApp)app).createContextHandler();
+ return ch;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public static String getOriginId(Bundle contributor, String path)
+ {
+ return contributor.getSymbolicName() + "-" + contributor.getVersion().toString() + (path.startsWith("/") ? path : "/" + path);
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java
new file mode 100644
index 0000000000..149aa99b80
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java
@@ -0,0 +1,169 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.DeploymentManager;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
+import org.eclipse.jetty.osgi.boot.utils.EventSender;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+
+
+/**
+ * BundleContextProvider
+ *
+ * Handles deploying bundles that define a context xml file for configuring them.
+ *
+ *
+ */
+public class BundleContextProvider extends AbstractContextProvider implements BundleProvider
+{
+ private static final Logger LOG = Log.getLogger(AbstractContextProvider.class);
+
+
+ private Map<String, App> _appMap = new HashMap<String, App>();
+
+ private Map<Bundle, List<App>> _bundleMap = new HashMap<Bundle, List<App>>();
+
+ private ServiceRegistration _serviceRegForBundles;
+
+
+
+
+ /* ------------------------------------------------------------ */
+ public BundleContextProvider(ServerInstanceWrapper wrapper)
+ {
+ super(wrapper);
+ }
+
+
+ /* ------------------------------------------------------------ */
+ @Override
+ protected void doStart() throws Exception
+ {
+ //register as an osgi service for deploying contexts defined in a bundle, advertising the name of the jetty Server instance we are related to
+ Dictionary<String,String> properties = new Hashtable<String,String>();
+ properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
+ _serviceRegForBundles = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(BundleProvider.class.getName(), this, properties);
+ super.doStart();
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ protected void doStop() throws Exception
+ {
+ //unregister ourselves
+ if (_serviceRegForBundles != null)
+ {
+ try
+ {
+ _serviceRegForBundles.unregister();
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+ }
+
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param bundle
+ * @param contextFiles
+ * @return
+ */
+ public boolean bundleAdded (Bundle bundle) throws Exception
+ {
+ if (bundle == null)
+ return false;
+
+ String contextFiles = (String)bundle.getHeaders().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
+ if (contextFiles == null)
+ contextFiles = (String)bundle.getHeaders().get(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
+
+ if (contextFiles == null)
+ return false;
+
+ boolean added = false;
+ //bundle defines JETTY_CONTEXT_FILE_PATH header,
+ //a comma separated list of context xml files that each define a ContextHandler
+ //TODO: (could be WebAppContexts)
+ String[] tmp = contextFiles.split(",;");
+ for (String contextFile : tmp)
+ {
+ String originId = bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + "-"+contextFile;
+ OSGiApp app = new OSGiApp(getDeploymentManager(), this, originId, bundle, contextFile);
+ _appMap.put(originId,app);
+ List<App> apps = _bundleMap.get(bundle);
+ if (apps == null)
+ {
+ apps = new ArrayList<App>();
+ _bundleMap.put(bundle, apps);
+ }
+ apps.add(app);
+ getDeploymentManager().addApp(app);
+ }
+
+ return added; //true if even 1 context from this bundle was added
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Bundle has been removed. If it was a context we deployed, undeploy it.
+ * @param bundle
+ *
+ * @return true if this was a context we had deployed, false otherwise
+ */
+ public boolean bundleRemoved (Bundle bundle) throws Exception
+ {
+ List<App> apps = _bundleMap.remove(bundle);
+ boolean removed = false;
+ if (apps != null)
+ {
+ for (App app:apps)
+ {
+ _appMap.remove(app.getOriginId());
+ getDeploymentManager().removeApp(app);
+ removed = true;
+ }
+ }
+ return removed; //true if even 1 context was removed associated with this bundle
+ }
+
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleProvider.java
new file mode 100644
index 0000000000..c87c071bf5
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleProvider.java
@@ -0,0 +1,28 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import org.osgi.framework.Bundle;
+
+public interface BundleProvider
+{
+ public boolean bundleAdded (Bundle bundle) throws Exception;
+
+ public boolean bundleRemoved (Bundle bundle) throws Exception;
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java
new file mode 100644
index 0000000000..2a5e6e3cd0
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java
@@ -0,0 +1,238 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
+import org.eclipse.jetty.osgi.boot.utils.EventSender;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceRegistration;
+
+
+
+/**
+ * BundleWebAppProvider
+ *
+ * A Jetty Provider that knows how to deploy a WebApp contained inside a Bundle.
+ *
+ */
+public class BundleWebAppProvider extends AbstractWebAppProvider implements BundleProvider
+{
+ private static final Logger LOG = Log.getLogger(AbstractWebAppProvider.class);
+
+ /**
+ * Map of Bundle to App. Used when a Bundle contains a webapp.
+ */
+ private Map<Bundle, App> _bundleMap = new HashMap<Bundle, App>();
+
+ private ServiceRegistration _serviceRegForBundles;
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param wrapper
+ */
+ public BundleWebAppProvider (ServerInstanceWrapper wrapper)
+ {
+ super(wrapper);
+ }
+
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
+ */
+ protected void doStart() throws Exception
+ {
+ //register as an osgi service for deploying bundles, advertising the name of the jetty Server instance we are related to
+ Dictionary<String,String> properties = new Hashtable<String,String>();
+ properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
+ _serviceRegForBundles = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(BundleProvider.class.getName(), this, properties);
+ super.doStart();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
+ */
+ @Override
+ protected void doStop() throws Exception
+ {
+ //unregister ourselves
+ if (_serviceRegForBundles != null)
+ {
+ try
+ {
+ _serviceRegForBundles.unregister();
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+
+ super.doStop();
+ }
+
+
+
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * A bundle has been added that could be a webapp
+ * @param bundle
+ */
+ public boolean bundleAdded (Bundle bundle) throws Exception
+ {
+ if (bundle == null)
+ return false;
+
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
+ String contextPath = null;
+ try
+ {
+ Dictionary headers = bundle.getHeaders();
+
+ //does the bundle have a OSGiWebappConstants.JETTY_WAR_FOLDER_PATH
+ if (headers.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH) != null)
+ {
+ String base = (String)headers.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
+ contextPath = getContextPath(bundle);
+ String originId = getOriginId(bundle, base);
+
+ //TODO : we don't know whether an app is actually deployed, as deploymentManager swallows all
+ //exceptions inside the impl of addApp. Need to send the Event and also register as a service
+ //only if the deployment succeeded
+ OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
+ app.setWebAppPath(base);
+ app.setContextPath(contextPath);
+ _bundleMap.put(bundle, app);
+ getDeploymentManager().addApp(app);
+ return true;
+ }
+
+
+ //does the bundle have a WEB-INF/web.xml
+ if (bundle.getEntry("/WEB-INF/web.xml") != null)
+ {
+ String base = ".";
+ contextPath = getContextPath(bundle);
+ String originId = getOriginId(bundle, base);
+
+ OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
+ app.setContextPath(contextPath);
+ app.setWebAppPath(base);
+ _bundleMap.put(bundle, app);
+ getDeploymentManager().addApp(app);
+ return true;
+ }
+
+ //does the bundle define a OSGiWebappConstants.RFC66_WEB_CONTEXTPATH
+ if (headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) != null)
+ {
+ //Could be a static webapp with no web.xml
+ String base = ".";
+ contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
+ String originId = getOriginId(bundle,base);
+
+ OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
+ app.setContextPath(contextPath);
+ app.setWebAppPath(base);
+ _bundleMap.put(bundle, app);
+ getDeploymentManager().addApp(app);
+ return true;
+ }
+
+ return false;
+ }
+ catch (Exception e)
+ {
+
+ throw e;
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Bundle has been removed. If it was a webapp we deployed, undeploy it.
+ * @param bundle
+ *
+ * @return true if this was a webapp we had deployed, false otherwise
+ */
+ public boolean bundleRemoved (Bundle bundle) throws Exception
+ {
+ App app = _bundleMap.remove(bundle);
+ if (app != null)
+ {
+ getDeploymentManager().removeApp(app);
+ return true;
+ }
+ return false;
+ }
+
+
+
+
+
+ /* ------------------------------------------------------------ */
+ private static String getContextPath(Bundle bundle)
+ {
+ Dictionary<?, ?> headers = bundle.getHeaders();
+ String contextPath = (String) headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
+ if (contextPath == null)
+ {
+ // extract from the last token of the bundle's location:
+ // (really ?could consider processing the symbolic name as an alternative
+ // the location will often reflect the version.
+ // maybe this is relevant when the file is a war)
+ String location = bundle.getLocation();
+ String toks[] = location.replace('\\', '/').split("/");
+ contextPath = toks[toks.length - 1];
+ // remove .jar, .war etc:
+ int lastDot = contextPath.lastIndexOf('.');
+ if (lastDot != -1)
+ contextPath = contextPath.substring(0, lastDot);
+ }
+ if (!contextPath.startsWith("/"))
+ contextPath = "/" + contextPath;
+
+ return contextPath;
+ }
+
+
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
index 777ebdf608..2ed6e7bf5e 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
@@ -66,8 +66,6 @@ public class JettyBootstrapActivator implements BundleActivator
private ServiceRegistration _registeredServer;
- private Server _server;
-
private JettyContextHandlerServiceTracker _jettyContextHandlerTracker;
private PackageAdminServiceTracker _packageAdminServiceTracker;
@@ -95,20 +93,21 @@ public class JettyBootstrapActivator implements BundleActivator
// should activate.
_packageAdminServiceTracker = new PackageAdminServiceTracker(context);
- // track Server instances that we should support as deployment targets
+ // track jetty Server instances that we should support as deployment targets
_jettyServerServiceTracker = new JettyServerServiceTracker();
context.addServiceListener(_jettyServerServiceTracker, "(objectclass=" + Server.class.getName() + ")");
// track ContextHandler class instances and deploy them to one of the known Servers
- _jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(_jettyServerServiceTracker);
+ _jettyContextHandlerTracker = new JettyContextHandlerServiceTracker();
context.addServiceListener(_jettyContextHandlerTracker, "(objectclass=" + ContextHandler.class.getName() + ")");
// Create a default jetty instance right now.
DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context);
// track Bundles and deploy those that represent webapps to one of the known Servers
- _webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer());
- _webBundleTracker.open();
+ WebBundleTrackerCustomizer customizer = new WebBundleTrackerCustomizer();
+ _webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, customizer);
+ customizer.setAndOpenWebBundleTracker(_webBundleTracker);
}
/**
@@ -129,7 +128,6 @@ public class JettyBootstrapActivator implements BundleActivator
}
if (_jettyContextHandlerTracker != null)
{
- _jettyContextHandlerTracker.stop();
context.removeServiceListener(_jettyContextHandlerTracker);
_jettyContextHandlerTracker = null;
}
@@ -163,10 +161,6 @@ public class JettyBootstrapActivator implements BundleActivator
}
finally
{
- if (_server != null)
- {
- _server.stop();
- }
INSTANCE = null;
}
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiDeployer.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiDeployer.java
new file mode 100644
index 0000000000..5f9321ebc9
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiDeployer.java
@@ -0,0 +1,61 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.bindings.StandardDeployer;
+import org.eclipse.jetty.deploy.graph.Node;
+import org.eclipse.jetty.osgi.boot.utils.EventSender;
+
+
+/**
+ * OSGiDeployer
+ *
+ *
+ */
+public class OSGiDeployer extends StandardDeployer
+{
+
+ /* ------------------------------------------------------------ */
+ public void processBinding(Node node, App app) throws Exception
+ {
+ //TODO how to NOT send this event if its not a webapp:
+ //OSGi Enterprise Spec only wants an event sent if its a webapp bundle (ie not a ContextHandler)
+ if (!(app instanceof AbstractOSGiApp))
+ {
+ super.processBinding(node,app);
+ }
+ else
+ {
+ EventSender.getInstance().send(EventSender.DEPLOYING_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
+ try
+ {
+ super.processBinding(node,app);
+ ((AbstractOSGiApp)app).registerAsOSGiService();
+ EventSender.getInstance().send(EventSender.DEPLOYED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
+ }
+ catch (Exception e)
+ {
+ EventSender.getInstance().send(EventSender.FAILED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
+ throw e;
+ }
+ }
+
+ }
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiMetaInfConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiMetaInfConfiguration.java
new file mode 100644
index 0000000000..bacc8ea9a0
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiMetaInfConfiguration.java
@@ -0,0 +1,116 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
+import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.MetaInfConfiguration;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.osgi.framework.Bundle;
+
+public class OSGiMetaInfConfiguration extends MetaInfConfiguration
+{
+ private static final Logger LOG = Log.getLogger(OSGiMetaInfConfiguration.class);
+
+
+ /**
+ * Inspect bundle fragments associated with the bundle of the webapp for web-fragment, resources, tlds.
+ *
+ * @see org.eclipse.jetty.webapp.MetaInfConfiguration#preConfigure(org.eclipse.jetty.webapp.WebAppContext)
+ */
+ @Override
+ public void preConfigure(final WebAppContext context) throws Exception
+ {
+ List<Resource> frags = (List<Resource>) context.getAttribute(METAINF_FRAGMENTS);
+ List<Resource> resfrags = (List<Resource>) context.getAttribute(METAINF_RESOURCES);
+ List<Resource> tldfrags = (List<Resource>) context.getAttribute(METAINF_TLDS);
+
+ Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles((Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE));
+ //TODO not convinced we need to do this, as we added any fragment jars to the MetaData.webInfJars in OSGiWebInfConfiguration,
+ //so surely the web-fragments and resources tlds etc can be discovered normally?
+ for (Bundle frag : fragments)
+ {
+ URL webFrag = frag.getEntry("/META-INF/web-fragment.xml");
+ Enumeration<URL> resEnum = frag.findEntries("/META-INF/resources", "*", true);
+ Enumeration<URL> tldEnum = frag.findEntries("/META-INF", "*.tld", false);
+ if (webFrag != null || (resEnum != null && resEnum.hasMoreElements()) || (tldEnum != null && tldEnum.hasMoreElements()))
+ {
+ try
+ {
+ if (webFrag != null)
+ {
+ if (frags == null)
+ {
+ frags = new ArrayList<Resource>();
+ context.setAttribute(METAINF_FRAGMENTS, frags);
+ }
+ frags.add(Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(frag).toURI()));
+ }
+ if (resEnum != null && resEnum.hasMoreElements())
+ {
+ URL resourcesEntry = frag.getEntry("/META-INF/resources/");
+ if (resourcesEntry == null)
+ {
+ // probably we found some fragments to a
+ // bundle.
+ // those are already contributed.
+ // so we skip this.
+ }
+ else
+ {
+ if (resfrags == null)
+ {
+ resfrags = new ArrayList<Resource>();
+ context.setAttribute(METAINF_RESOURCES, resfrags);
+ }
+ resfrags.add(Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(resourcesEntry)));
+ }
+ }
+ if (tldEnum != null && tldEnum.hasMoreElements())
+ {
+ if (tldfrags == null)
+ {
+ tldfrags = new ArrayList<Resource>();
+ context.setAttribute(METAINF_TLDS, tldfrags);
+ }
+ while (tldEnum.hasMoreElements())
+ {
+ URL tldUrl = tldEnum.nextElement();
+ tldfrags.add(Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(tldUrl)));
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Unable to locate the bundle " + frag.getBundleId(), e);
+ }
+ }
+ }
+
+ super.preConfigure(context);
+ }
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiServerConstants.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiServerConstants.java
index 53b35c7fd6..2f9df55ad8 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiServerConstants.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiServerConstants.java
@@ -23,6 +23,38 @@ package org.eclipse.jetty.osgi.boot;
*/
public class OSGiServerConstants
{
+ /**
+ * Usual system property used as the hostname for a typical jetty
+ * configuration.
+ */
+ public static final String JETTY_HOME = "jetty.home";
+
+ /**
+ * System property to point to a bundle that embeds a jetty configuration
+ * and that jetty configuration should be the default jetty server. First we
+ * look for jetty.home. If we don't find it then we look for this property.
+ */
+ public static final String JETTY_HOME_BUNDLE = "jetty.home.bundle";
+
+ /**
+ * Usual system property used as the hostname for a typical jetty
+ * configuration.
+ */
+ public static final String JETTY_HOST = "jetty.host";
+
+ /**
+ * Usual system property used as the port for http for a typical jetty
+ * configuration.
+ */
+ public static final String JETTY_PORT = "jetty.port";
+
+ /**
+ * Usual system property used as the port for https for a typical jetty
+ * configuration.
+ */
+ public static final String JETTY_PORT_SSL = "jetty.port.ssl";
+
+
//for managed jetty instances, name of the configuration parameters
/**
* PID of the jetty servers's ManagedFactory
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiUndeployer.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiUndeployer.java
new file mode 100644
index 0000000000..ac068741d0
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiUndeployer.java
@@ -0,0 +1,44 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.bindings.StandardUndeployer;
+import org.eclipse.jetty.deploy.graph.Node;
+import org.eclipse.jetty.osgi.boot.utils.EventSender;
+
+
+
+
+/**
+ * OSGiUndeployer
+ *
+ *
+ */
+public class OSGiUndeployer extends StandardUndeployer
+{
+ /* ------------------------------------------------------------ */
+ public void processBinding(Node node, App app) throws Exception
+ {
+ EventSender.getInstance().send(EventSender.UNDEPLOYING_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
+ super.processBinding(node,app);
+ EventSender.getInstance().send(EventSender.UNDEPLOYED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
+ ((AbstractOSGiApp)app).deregisterAsOSGiService();
+ }
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java
new file mode 100644
index 0000000000..80deac4fb5
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java
@@ -0,0 +1,273 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.util.regex.Pattern;
+
+import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
+import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.resource.ResourceCollection;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.webapp.WebInfConfiguration;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+
+
+/**
+ * OSGiWebInfConfiguration
+ *
+ * Handle adding resources found in bundle fragments, and add them into the
+ */
+public class OSGiWebInfConfiguration extends WebInfConfiguration
+{
+ private static final Logger LOG = Log.getLogger(WebInfConfiguration.class);
+
+
+ public static final String CONTAINER_BUNDLE_PATTERN = "org.eclipse.jetty.server.webapp.containerIncludeBundlePattern";
+
+
+ /**
+ * Check to see if there have been any bundle symbolic names added of bundles that should be
+ * regarded as being on the container classpath, and scanned for fragments, tlds etc etc.
+ * This can be defined in:
+ * <ol>
+ * <li>SystemProperty SYS_PROP_TLD_BUNDLES</li>
+ * <li>DeployerManager.setContextAttribute CONTAINER_BUNDLE_PATTERN</li>
+ * </ol>
+ *
+ * We also allow individual bundles to specify particular bundles that might include TLDs via the Require-Tlds
+ * MANIFEST.MF header. This is processed in the TagLibOSGiConfiguration class.
+ *
+ * @see org.eclipse.jetty.webapp.WebInfConfiguration#preConfigure(org.eclipse.jetty.webapp.WebAppContext)
+ */
+ @Override
+ public void preConfigure(final WebAppContext context) throws Exception
+ {
+ super.preConfigure(context);
+
+ //Check to see if there have been any bundle symbolic names added of bundles that should be
+ //regarded as being on the container classpath, and scanned for fragments, tlds etc etc.
+ //This can be defined in:
+ // 1. SystemProperty SYS_PROP_TLD_BUNDLES
+ // 2. DeployerManager.setContextAttribute CONTAINER_BUNDLE_PATTERN
+ String tmp = (String)context.getAttribute(CONTAINER_BUNDLE_PATTERN);
+ Pattern pattern = (tmp==null?null:Pattern.compile(tmp));
+ List<String> names = new ArrayList<String>();
+ tmp = System.getProperty("org.eclipse.jetty.osgi.tldbundles");
+ if (tmp != null)
+ {
+ StringTokenizer tokenizer = new StringTokenizer(tmp, ", \n\r\t", false);
+ while (tokenizer.hasMoreTokens())
+ names.add(tokenizer.nextToken());
+ }
+
+ HashSet<Resource> matchingResources = new HashSet<Resource>();
+ if ( !names.isEmpty() || pattern != null)
+ {
+ Bundle[] bundles = FrameworkUtil.getBundle(OSGiWebInfConfiguration.class).getBundleContext().getBundles();
+
+ for (Bundle bundle : bundles)
+ {
+ if (pattern != null)
+ {
+ // if bundle symbolic name matches the pattern
+ if (pattern.matcher(bundle.getSymbolicName()).matches())
+ {
+ //get the file location of the jar and put it into the list of container jars that will be scanned for stuff (including tlds)
+ matchingResources.addAll(getBundleAsResource(bundle));
+ }
+ }
+ if (names != null)
+ {
+ //if there is an explicit bundle name, then check if it matches
+ if (names.contains(bundle.getSymbolicName()))
+ matchingResources.addAll(getBundleAsResource(bundle));
+ }
+ }
+ }
+
+ for (Resource r:matchingResources)
+ {
+ context.getMetaData().addContainerJar(r);
+ }
+ }
+
+
+
+ /**
+ * Consider the fragment bundles associated with the bundle of the webapp being deployed.
+ *
+ *
+ * @see org.eclipse.jetty.webapp.WebInfConfiguration#findJars(org.eclipse.jetty.webapp.WebAppContext)
+ */
+ @Override
+ protected List<Resource> findJars (WebAppContext context)
+ throws Exception
+ {
+ List<Resource> mergedResources = new ArrayList<Resource>();
+ //get jars from WEB-INF/lib if there are any
+ List<Resource> webInfJars = super.findJars(context);
+ if (webInfJars != null)
+ mergedResources.addAll(webInfJars);
+
+ //add fragment jars as if in WEB-INF/lib of the associated webapp
+ Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles((Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE));
+ for (Bundle frag : fragments)
+ {
+ File fragFile = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(frag);
+ mergedResources.add(Resource.newResource(fragFile.toURI()));
+ }
+
+ return mergedResources;
+ }
+
+
+ /**
+ * Allow fragments to supply some resources that are added to the baseResource of the webapp.
+ *
+ * The resources can be either prepended or appended to the baseResource.
+ *
+ * @see org.eclipse.jetty.webapp.WebInfConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
+ */
+ @Override
+ public void configure(WebAppContext context) throws Exception
+ {
+ TreeMap<String, Resource> patchResourcesPath = new TreeMap<String, Resource>();
+ TreeMap<String, Resource> appendedResourcesPath = new TreeMap<String, Resource>();
+
+ Bundle bundle = (Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
+ if (bundle != null)
+ {
+ //TODO anything we need to do to improve PackageAdminServiceTracker?
+ Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(bundle);
+ if (fragments != null && fragments.length != 0)
+ {
+ // sorted extra resource base found in the fragments.
+ // the resources are either overriding the resourcebase found in the
+ // web-bundle
+ // or appended.
+ // amongst each resource we sort them according to the alphabetical
+ // order
+ // of the name of the internal folder and the symbolic name of the
+ // fragment.
+ // this is useful to make sure that the lookup path of those
+ // resource base defined by fragments is always the same.
+ // This natural order could be abused to define the order in which
+ // the base resources are
+ // looked up.
+ for (Bundle frag : fragments)
+ {
+ String fragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH);
+ String patchFragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH);
+ if (fragFolder != null)
+ {
+ URL fragUrl = frag.getEntry(fragFolder);
+ if (fragUrl == null) { throw new IllegalArgumentException("Unable to locate " + fragFolder
+ + " inside "
+ + " the fragment '"
+ + frag.getSymbolicName()
+ + "'"); }
+ fragUrl = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(fragUrl);
+ String key = fragFolder.startsWith("/") ? fragFolder.substring(1) : fragFolder;
+ appendedResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(fragUrl));
+ }
+ if (patchFragFolder != null)
+ {
+ URL patchFragUrl = frag.getEntry(patchFragFolder);
+ if (patchFragUrl == null)
+ {
+ throw new IllegalArgumentException("Unable to locate " + patchFragUrl
+ + " inside fragment '"+frag.getSymbolicName()+ "'");
+ }
+ patchFragUrl = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(patchFragUrl);
+ String key = patchFragFolder.startsWith("/") ? patchFragFolder.substring(1) : patchFragFolder;
+ patchResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(patchFragUrl));
+ }
+ }
+ if (!appendedResourcesPath.isEmpty())
+ context.setAttribute(WebInfConfiguration.RESOURCE_URLS, new ArrayList<Resource>(appendedResourcesPath.values()));
+ }
+ }
+
+ super.configure(context);
+
+ // place the patch resources at the beginning of the contexts's resource base
+ if (!patchResourcesPath.isEmpty())
+ {
+ Resource[] resources = new Resource[1+patchResourcesPath.size()];
+ ResourceCollection mergedResources = new ResourceCollection (patchResourcesPath.values().toArray(new Resource[patchResourcesPath.size()]));
+ System.arraycopy(patchResourcesPath.values().toArray(new Resource[patchResourcesPath.size()]), 0, resources, 0, patchResourcesPath.size());
+ resources[resources.length-1] = context.getBaseResource();
+ context.setBaseResource(new ResourceCollection(resources));
+ }
+
+ }
+
+
+
+ /**
+ * Resolves the bundle. Usually that would be a single URL per bundle. But we do some more work if there are jars
+ * embedded in the bundle.
+ */
+ private List<Resource> getBundleAsResource(Bundle bundle)
+ throws Exception
+ {
+ List<Resource> resources = new ArrayList<Resource>();
+
+ File file = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle);
+ if (file.isDirectory())
+ {
+ for (File f : file.listFiles())
+ {
+ if (f.getName().endsWith(".jar") && f.isFile())
+ {
+ resources.add(Resource.newResource(f));
+ }
+ else if (f.isDirectory() && f.getName().equals("lib"))
+ {
+ for (File f2 : file.listFiles())
+ {
+ if (f2.getName().endsWith(".jar") && f2.isFile())
+ {
+ resources.add(Resource.newResource(f));
+ }
+ }
+ }
+ }
+ resources.add(Resource.newResource(file)); //TODO really???
+ }
+ else
+ {
+ resources.add(Resource.newResource(file));
+ }
+
+ return resources;
+ }
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java
index 3cb21232ed..1908eb2de5 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebappConstants.java
@@ -23,6 +23,20 @@ package org.eclipse.jetty.osgi.boot;
*/
public class OSGiWebappConstants
{
+ /** service property osgi.web.symbolicname. See OSGi r4 */
+ public static final String OSGI_WEB_SYMBOLICNAME = "osgi.web.symbolicname";
+
+ /** service property osgi.web.symbolicname. See OSGi r4 */
+ public static final String OSGI_WEB_VERSION = "osgi.web.version";
+
+ /** service property osgi.web.contextpath. See OSGi r4 */
+ public static final String OSGI_WEB_CONTEXTPATH = "osgi.web.contextpath";
+
+ /** See OSGi r4 p.427 */
+ public static final String OSGI_BUNDLECONTEXT = "osgi-bundlecontext";
+
+
+
/** url scheme to deploy war file as bundled webapp */
public static final String RFC66_WAR_URL_SCHEME = "war";
@@ -59,32 +73,60 @@ public class OSGiWebappConstants
* this will override static resources with the same name in the web-bundle. */
public static final String JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH = "Jetty-WarPatchFragmentFolderPath";
- // OSGi ContextHandler service properties.
- /** web app context path */
+
+ /**
+ * web app context path
+ * @deprecated see RFC66_WEB_CONTEXTPATH
+ */
public static final String SERVICE_PROP_CONTEXT_PATH = "contextPath";
- /** Path to the web application base folder */
+
+ /**
+ * Path to the web application base folder
+ * @deprecated see JETTY_WAR_FOLDER_PATH
+ */
public static final String SERVICE_PROP_WAR = "war";
- /** Extra classpath */
+ /**
+ * Extra classpath
+ * @deprecated see JETTY_EXTRA_CLASSPATH
+ */
public static final String SERVICE_PROP_EXTRA_CLASSPATH = "extraClasspath";
+
+ public static final String JETTY_EXTRA_CLASSPATH = "Jetty-extraClasspath";
- /** jetty context file path */
+ /**
+ * jetty context file path
+ * @deprecated see JETTY_CONTEXT_FILE_PATH
+ */
public static final String SERVICE_PROP_CONTEXT_FILE_PATH = "contextFilePath";
- /** web.xml file path */
+ /**
+ * web.xml file path
+ * @deprecated see JETTY_WEB_XML_PATH
+ */
public static final String SERVICE_PROP_WEB_XML_PATH = "webXmlFilePath";
+
+ public static final String JETTY_WEB_XML_PATH = "Jetty-WebXmlFilePath";
- /** defaultweb.xml file path */
+ /**
+ * defaultweb.xml file path
+ * @deprecated see JETTY_DEFAULT_WEB_XML_PATH
+ */
public static final String SERVICE_PROP_DEFAULT_WEB_XML_PATH = "defaultWebXmlFilePath";
+
+ public static final String JETTY_DEFAULT_WEB_XML_PATH = "Jetty-defaultWebXmlFilePath";
/**
* path to the base folder that overrides the computed bundle installation
* location if not null useful to install webapps or jetty context files
* that are in fact not embedded in a bundle
+ * @deprecated see JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE
*/
public static final String SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE = "thisBundleInstall";
+ public static final String JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE = "Jetty-bundleInstall";
+
/**
* Comma separated list of bundles that contain tld file used by the webapp.
*/
@@ -94,4 +136,14 @@ public class OSGiWebappConstants
* Both the name of the manifest header and the name of the service property.
*/
public static final String SERVICE_PROP_REQUIRE_TLD_BUNDLE = REQUIRE_TLD_BUNDLE;
+
+ public static final String WATERMARK = "o.e.j.o.b.watermark";
+
+ /**
+ * Set of extra dirs that must not be served by osgi webapps
+ */
+ public static final String[] DEFAULT_PROTECTED_OSGI_TARGETS = {"/osgi-inf", "/osgi-opts"};
+
+
+
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceContextProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceContextProvider.java
new file mode 100644
index 0000000000..b7630cb188
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceContextProvider.java
@@ -0,0 +1,186 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.AppProvider;
+import org.eclipse.jetty.deploy.DeploymentManager;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * ServiceContextProvider
+ *
+ *
+ */
+public class ServiceContextProvider extends AbstractContextProvider implements ServiceProvider
+{
+ private static final Logger LOG = Log.getLogger(AbstractContextProvider.class);
+
+ private Map<ServiceReference, App> _serviceMap = new HashMap<ServiceReference, App>();
+
+ private ServiceRegistration _serviceRegForServices;
+
+
+ /**
+ * ServiceApp
+ *
+ *
+ */
+ public class ServiceApp extends OSGiApp
+ {
+ public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String contextFile, String originId)
+ {
+ super(manager, provider, bundle, properties, contextFile, originId);
+ }
+
+ public ServiceApp(DeploymentManager manager, AppProvider provider, String originId, Bundle bundle, String contextFile)
+ {
+ super(manager, provider, originId, bundle, contextFile);
+ }
+
+ @Override
+ public void registerAsOSGiService() throws Exception
+ {
+ //not applicable for apps that are already services
+ }
+
+ @Override
+ protected void deregisterAsOSGiService() throws Exception
+ {
+ //not applicable for apps that are already services
+ }
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ public ServiceContextProvider(ServerInstanceWrapper wrapper)
+ {
+ super(wrapper);
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public boolean serviceAdded (ServiceReference serviceRef, ContextHandler context)
+ {
+ if (context == null || serviceRef == null)
+ return false;
+
+ String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
+ if (watermark != null && !"".equals(watermark))
+ return false; //this service represents a contexthandler that has already been registered as a service by another of our deployers
+
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
+ try
+ {
+ //See if there is a context file to apply to this pre-made context
+ String contextFile = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
+ if (contextFile == null)
+ contextFile = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
+
+ String[] keys = serviceRef.getPropertyKeys();
+ Dictionary properties = new Hashtable<String, Object>();
+ if (keys != null)
+ {
+ for (String key:keys)
+ properties.put(key, serviceRef.getProperty(key));
+ }
+ Bundle bundle = serviceRef.getBundle();
+ String originId = bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + "-"+contextFile;
+ ServiceApp app = new ServiceApp(getDeploymentManager(), this, bundle, properties, contextFile, originId);
+ app.setHandler(context); //set the pre=made ContextHandler instance
+ _serviceMap.put(serviceRef, app);
+ getDeploymentManager().addApp(app);
+ return true;
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public boolean serviceRemoved (ServiceReference serviceRef, ContextHandler context)
+ {
+
+ if (context == null || serviceRef == null)
+ return false;
+
+ String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
+ if (watermark != null && !"".equals(watermark))
+ return false; //this service represents a contexthandler that will be deregistered as a service by another of our deployers
+
+ App app = _serviceMap.remove(serviceRef);
+ if (app != null)
+ {
+ getDeploymentManager().removeApp(app);
+ return true;
+ }
+
+ return false;
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ @Override
+ protected void doStart() throws Exception
+ {
+ //register as an osgi service for deploying contexts defined in a bundle, advertising the name of the jetty Server instance we are related to
+ Dictionary<String,String> properties = new Hashtable<String,String>();
+ properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
+
+ //register as an osgi service for deploying contexts, advertising the name of the jetty Server instance we are related to
+ _serviceRegForServices = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ServiceProvider.class.getName(), this, properties);
+ super.doStart();
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ protected void doStop() throws Exception
+ {
+ //unregister ourselves
+ if (_serviceRegForServices != null)
+ {
+ try
+ {
+ _serviceRegForServices.unregister();
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+ super.doStop();
+ }
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceProvider.java
new file mode 100644
index 0000000000..f2304c6b13
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceProvider.java
@@ -0,0 +1,29 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.osgi.framework.ServiceReference;
+
+public interface ServiceProvider
+{
+ public boolean serviceAdded (ServiceReference ref, ContextHandler handler) throws Exception;
+
+ public boolean serviceRemoved (ServiceReference ref, ContextHandler handler) throws Exception;
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceWebAppProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceWebAppProvider.java
new file mode 100644
index 0000000000..e3f97f0913
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/ServiceWebAppProvider.java
@@ -0,0 +1,248 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.eclipse.jetty.deploy.App;
+import org.eclipse.jetty.deploy.AppProvider;
+import org.eclipse.jetty.deploy.DeploymentManager;
+import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
+import org.eclipse.jetty.osgi.boot.utils.EventSender;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+
+
+/**
+ * ServiceWebAppProvider
+ *
+ * Jetty Provider that knows how to deploy a WebApp that has been registered as an OSGi service.
+ *
+ */
+public class ServiceWebAppProvider extends AbstractWebAppProvider implements ServiceProvider
+{
+ private static final Logger LOG = Log.getLogger(AbstractWebAppProvider.class);
+
+
+ /**
+ * Map of ServiceRef to App. Used when it is an osgi service that is a WebAppContext.
+ */
+ private Map<ServiceReference, App> _serviceMap = new HashMap<ServiceReference, App>();
+
+ private ServiceRegistration _serviceRegForServices;
+
+
+ /**
+ * ServiceApp
+ *
+ *
+ */
+ public class ServiceApp extends OSGiApp
+ {
+
+ public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId)
+ {
+ super(manager, provider, bundle, properties, originId);
+ }
+
+ public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
+ {
+ super(manager, provider, bundle, originId);
+ }
+
+ @Override
+ public void registerAsOSGiService() throws Exception
+ {
+ //not applicable for apps that are already services
+ }
+
+ @Override
+ protected void deregisterAsOSGiService() throws Exception
+ {
+ //not applicable for apps that are already services
+ }
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param wrapper
+ */
+ public ServiceWebAppProvider (ServerInstanceWrapper wrapper)
+ {
+ super(wrapper);
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * A webapp that was deployed as an osgi service has been added,
+ * and we want to deploy it.
+ *
+ * @param context the webapp
+ */
+ public boolean serviceAdded (ServiceReference serviceRef, ContextHandler context)
+ {
+ if (context == null || !(context instanceof WebAppContext))
+ return false;
+
+ String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
+ if (watermark != null && !"".equals(watermark))
+ return false; //this service represents a webapp that has already been registered as a service by another of our deployers
+
+
+ WebAppContext webApp = (WebAppContext)context;
+ Dictionary properties = new Hashtable<String,String>();
+
+ String contextPath = (String)serviceRef.getProperty(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
+ if (contextPath == null)
+ contextPath = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH);
+ if (contextPath == null)
+ return false; //No context path
+
+ String base = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
+ if (base == null)
+ base = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_WAR);
+
+ if (base == null)
+ return false; //No webapp base
+
+ String webdefaultXml = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH);
+ if (webdefaultXml == null)
+ webdefaultXml = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_DEFAULT_WEB_XML_PATH);
+ if (webdefaultXml != null)
+ properties.put(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH, webdefaultXml);
+
+ String webXml = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH);
+ if (webXml == null)
+ webXml = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_WEB_XML_PATH);
+ if (webXml != null)
+ properties.put(OSGiWebappConstants.JETTY_WEB_XML_PATH, webXml);
+
+ String extraClassPath = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH);
+ if (extraClassPath == null)
+ extraClassPath = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH);
+ if (extraClassPath != null)
+ properties.put(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH, extraClassPath);
+
+ String bundleInstallOverride = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
+ if (bundleInstallOverride == null)
+ bundleInstallOverride = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE);
+ if (bundleInstallOverride != null)
+ properties.put(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE, bundleInstallOverride);
+
+ String requiredTlds = (String)serviceRef.getProperty(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
+ if (requiredTlds == null)
+ requiredTlds = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE);
+ if (requiredTlds != null)
+ properties.put(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requiredTlds);
+
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
+ try
+ {
+ String originId = getOriginId(serviceRef.getBundle(), base);
+ ServiceApp app = new ServiceApp(getDeploymentManager(), this, serviceRef.getBundle(), properties, originId);
+ app.setContextPath(contextPath);
+ app.setWebAppPath(base);
+ app.setWebAppContext(webApp); //set the pre=made webapp instance
+ _serviceMap.put(serviceRef, app);
+ getDeploymentManager().addApp(app);
+ return true;
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param context the webapp
+ */
+ public boolean serviceRemoved (ServiceReference serviceRef, ContextHandler context)
+ {
+ if (context == null || !(context instanceof WebAppContext))
+ return false;
+
+ String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
+ if (watermark != null && !"".equals(watermark))
+ return false; //this service represents a contexthandler that will be deregistered as a service by another of our deployers
+
+ App app = _serviceMap.remove(serviceRef);
+ if (app != null)
+ {
+ getDeploymentManager().removeApp(app);
+ return true;
+ }
+ return false;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
+ */
+ protected void doStart() throws Exception
+ {
+ //register as an osgi service for deploying bundles, advertising the name of the jetty Server instance we are related to
+ Dictionary<String,String> properties = new Hashtable<String,String>();
+ properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
+
+ //register as an osgi service for deploying contexts (discovered as osgi services), advertising the name of the jetty Server instance we are related to
+ _serviceRegForServices = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ServiceProvider.class.getName(), this, properties);
+ super.doStart();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
+ */
+ @Override
+ protected void doStop() throws Exception
+ {
+ //unregister ourselves
+ if (_serviceRegForServices != null)
+ {
+ try
+ {
+ _serviceRegForServices.unregister();
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+ super.doStop();
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
index 5624d07e62..ce8a51455f 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
@@ -28,6 +28,7 @@ import java.util.StringTokenizer;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
+import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.log.Log;
@@ -36,6 +37,9 @@ import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
/**
+ * DefaultJettyAtJettyHomeHelper
+ *
+ *
* Called by the {@link JettyBootstrapActivator} during the starting of the
* bundle. If the system property 'jetty.home' is defined and points to a
* folder, then setup the corresponding jetty server.
@@ -49,49 +53,20 @@ public class DefaultJettyAtJettyHomeHelper
* used to configure jetty. By default the value is 'etc/jetty.xml' when the
* path is relative the file is resolved relatively to jettyhome.
*/
- public static final String SYS_PROP_JETTY_ETC_FILES = OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS;
+ public static final String 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";
+ public static final String DEFAULT_JETTY_ETC_FILES = "etc/jetty.xml,etc/jetty-selector.xml,etc/jetty-deployer.xml";
/**
* Default location within bundle of a jetty home dir.
*/
public static final String DEFAULT_JETTYHOME = "/jettyhome";
-
+
+
+ /* ------------------------------------------------------------ */
/**
* Called by the JettyBootStrapActivator. If the system property jetty.home
* is defined and points to a folder, creates a corresponding jetty
@@ -116,8 +91,8 @@ public class DefaultJettyAtJettyHomeHelper
*/
public static void startJettyAtJettyHome(BundleContext bundleContext) throws Exception
{
- String jettyHomeSysProp = System.getProperty(SYS_PROP_JETTY_HOME);
- String jettyHomeBundleSysProp = System.getProperty(SYS_PROP_JETTY_HOME_BUNDLE);
+ String jettyHomeSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME);
+ String jettyHomeBundleSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME_BUNDLE);
File jettyHome = null;
Bundle jettyHomeBundle = null;
if (jettyHomeSysProp != null)
@@ -174,17 +149,18 @@ public class DefaultJettyAtJettyHomeHelper
// these properties usually are the ones passed to this type of
// configuration.
- setProperty(properties, SYS_PROP_JETTY_HOME, System.getProperty(SYS_PROP_JETTY_HOME));
- setProperty(properties, SYS_PROP_JETTY_HOST, System.getProperty(SYS_PROP_JETTY_HOST));
- setProperty(properties, SYS_PROP_JETTY_PORT, System.getProperty(SYS_PROP_JETTY_PORT));
- setProperty(properties, SYS_PROP_JETTY_PORT_SSL, System.getProperty(SYS_PROP_JETTY_PORT_SSL));
+ setProperty(properties, OSGiServerConstants.JETTY_HOME, System.getProperty(OSGiServerConstants.JETTY_HOME));
+ setProperty(properties, OSGiServerConstants.JETTY_HOST, System.getProperty(OSGiServerConstants.JETTY_HOST));
+ setProperty(properties, OSGiServerConstants.JETTY_PORT, System.getProperty(OSGiServerConstants.JETTY_PORT));
+ setProperty(properties, OSGiServerConstants.JETTY_PORT_SSL, System.getProperty(OSGiServerConstants.JETTY_PORT_SSL));
//register the Server instance as an OSGi service.
bundleContext.registerService(Server.class.getName(), server, properties);
- // hookNestedConnectorToBridgeServlet(server);
-
}
-
+
+
+
+ /* ------------------------------------------------------------ */
/**
* Minimum setup for the location of the configuration files given a
* jettyhome folder. Reads the system property jetty.etc.config.urls and
@@ -196,7 +172,7 @@ public class DefaultJettyAtJettyHomeHelper
*/
private static String getJettyConfigurationURLs(File jettyhome)
{
- String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES, "etc/jetty.xml");
+ String jettyetc = System.getProperty(JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
StringTokenizer tokenizer = new StringTokenizer(jettyetc, ";,", false);
StringBuilder res = new StringBuilder();
while (tokenizer.hasMoreTokens())
@@ -218,7 +194,9 @@ public class DefaultJettyAtJettyHomeHelper
}
return res.toString();
}
-
+
+
+ /* ------------------------------------------------------------ */
/**
* Minimum setup for the location of the configuration files given a
* configuration embedded inside a bundle. Reads the system property
@@ -230,7 +208,7 @@ public class DefaultJettyAtJettyHomeHelper
*/
private static String getJettyConfigurationURLs(Bundle configurationBundle)
{
- String files = System.getProperty(SYS_PROP_JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
+ String files = System.getProperty(JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
StringTokenizer tokenizer = new StringTokenizer(files, ";,", false);
StringBuilder res = new StringBuilder();
@@ -246,36 +224,39 @@ public class DefaultJettyAtJettyHomeHelper
else
{
//relative file path
- Enumeration<URL> enUrls = BundleFileLocatorHelper.DEFAULT.findEntries(configurationBundle, etcFile);
-
+ Enumeration<URL> enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, etcFile);
+
// default for org.eclipse.osgi.boot where we look inside
// jettyhome for the default embedded configuration.
// 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: "
+ String tmp = DEFAULT_JETTYHOME+etcFile;
+ enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, tmp);
+ LOG.info("Configuring jetty from bundle: "
+ configurationBundle.getSymbolicName()
- + " config: "+tmp);
+ + " with "+tmp);
}
if (enUrls == null || !enUrls.hasMoreElements())
{
- LOG.warn("Unable to locate a jetty configuration file for " + etcFile);
+ throw new IllegalStateException ("Unable to locate a jetty configuration file for " + etcFile);
}
if (enUrls != null)
{
while (enUrls.hasMoreElements())
{
- appendToCommaSeparatedList(res, enUrls.nextElement().toString());
+ URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getFileURL(enUrls.nextElement());
+ appendToCommaSeparatedList(res, url.toString());
}
}
}
}
return res.toString();
}
-
+
+
+ /* ------------------------------------------------------------ */
private static void appendToCommaSeparatedList(StringBuilder buffer, String value)
{
if (buffer.length() != 0)
@@ -284,7 +265,9 @@ public class DefaultJettyAtJettyHomeHelper
}
buffer.append(value);
}
-
+
+
+ /* ------------------------------------------------------------ */
private static void setProperty(Dictionary<String,String> properties, String key, String value)
{
if (value != null)
@@ -292,7 +275,9 @@ public class DefaultJettyAtJettyHomeHelper
properties.put(key, value);
}
}
-
+
+
+ /* ------------------------------------------------------------ */
/**
* recursively substitute the ${sysprop} by their actual system property.
* ${sysprop,defaultvalue} will use 'defaultvalue' as the value if no
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java
index 10f442f9c4..f9fb433ac9 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java
@@ -23,7 +23,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
@@ -31,16 +30,24 @@ import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
+import org.eclipse.jetty.deploy.AppLifeCycle;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
+import org.eclipse.jetty.deploy.bindings.StandardStarter;
+import org.eclipse.jetty.deploy.bindings.StandardStopper;
+import org.eclipse.jetty.osgi.boot.BundleContextProvider;
+import org.eclipse.jetty.osgi.boot.BundleWebAppProvider;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
-import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
+import org.eclipse.jetty.osgi.boot.OSGiDeployer;
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
+import org.eclipse.jetty.osgi.boot.OSGiUndeployer;
+import org.eclipse.jetty.osgi.boot.ServiceContextProvider;
+import org.eclipse.jetty.osgi.boot.ServiceWebAppProvider;
import org.eclipse.jetty.osgi.boot.internal.jsp.TldLocatableURLClassloader;
+import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.internal.webapp.LibExtClassLoaderHelper;
-import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleDeployerHelper;
+import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleTrackerCustomizer;
import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
-import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.IO;
@@ -66,6 +73,8 @@ public class ServerInstanceWrapper
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;
@@ -74,7 +83,7 @@ public class ServerInstanceWrapper
*/
private Server _server;
- private ContextHandlerCollection _ctxtHandler;
+ private ContextHandlerCollection _ctxtCollection;
/**
* This is the class loader that should be the parent classloader of any
@@ -84,21 +93,22 @@ public class ServerInstanceWrapper
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.
@@ -109,7 +119,9 @@ public class ServerInstanceWrapper
{
return _commonParentClassLoaderForWebapps;
}
-
+
+
+ /* ------------------------------------------------------------ */
/**
* @return The deployment manager registered on this server.
*/
@@ -117,33 +129,28 @@ public class ServerInstanceWrapper
{
return _deploymentManager;
}
-
+
+
+ /* ------------------------------------------------------------ */
/**
* @return The app provider registered on this server.
*/
- public OSGiAppProvider getOSGiAppProvider()
- {
- return _provider;
- }
-
public Server getServer()
{
return _server;
}
- public WebBundleDeployerHelper getWebBundleDeployerHelp()
- {
- return _webBundleDeployerHelper;
- }
-
+ /* ------------------------------------------------------------ */
/**
* @return The collection of context handlers
*/
public ContextHandlerCollection getContextHandlerCollection()
{
- return _ctxtHandler;
+ return _ctxtCollection;
}
-
+
+
+ /* ------------------------------------------------------------ */
public void start(Server server, Dictionary props) throws Exception
{
_server = server;
@@ -158,20 +165,20 @@ public class ServerInstanceWrapper
List<File> shared = sharedURLs != null ? extractFiles(sharedURLs) : null;
libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader(shared, null, server, JettyBootstrapActivator.class.getClassLoader());
+ if (LOG.isDebugEnabled()) LOG.debug("LibExtClassLoader = "+libExtClassLoader);
+
Thread.currentThread().setContextClassLoader(libExtClassLoader);
configure(server, props);
init();
- // now that we have an app provider we can call the registration
- // customizer.
-
URL[] jarsWithTlds = getJarsWithTlds();
_commonParentClassLoaderForWebapps = jarsWithTlds == null ? libExtClassLoader : new TldLocatableURLClassloader(libExtClassLoader, jarsWithTlds);
+
+ if (LOG.isDebugEnabled()) LOG.debug("common classloader = "+_commonParentClassLoaderForWebapps);
server.start();
- _webBundleDeployerHelper = new WebBundleDeployerHelper(this);
}
catch (Exception e)
{
@@ -194,7 +201,7 @@ public class ServerInstanceWrapper
}
}
-
+ /* ------------------------------------------------------------ */
public void stop()
{
try
@@ -209,7 +216,9 @@ public class ServerInstanceWrapper
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.
@@ -233,11 +242,19 @@ public class ServerInstanceWrapper
*/
private URL[] getJarsWithTlds() throws Exception
{
+
+ //Jars that are added onto the equivalent of the container classpath are:
+ // jstl jars: identified by the class WhenTag (and the boot-bundle manifest imports the jstl packages
+ // bundles identified by System property org.eclipse.jetty.osgi.tldbundles
+ // bundle symbolic name patterns defined in the DeploymentManager
+ //
+ // Any bundles mentioned in the Require-TldBundle manifest header of the webapp bundle MUST ALSO HAVE Import-Bundle
+ // in order to get them onto the classpath of the webapp.
+
ArrayList<URL> res = new ArrayList<URL>();
- WebBundleDeployerHelper.staticInit();// that is not looking great.
- for (WebappRegistrationCustomizer regCustomizer : WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS)
+ for (WebappRegistrationCustomizer regCustomizer : WebBundleTrackerCustomizer.JSP_REGISTRATION_HELPERS)
{
- URL[] urls = regCustomizer.getJarsWithTlds(_provider, WebBundleDeployerHelper.BUNDLE_FILE_LOCATOR_HELPER);
+ URL[] urls = regCustomizer.getJarsWithTlds(_deploymentManager, BundleFileLocatorHelperFactory.getFactory().getHelper());
for (URL url : urls)
{
if (!res.contains(url)) res.add(url);
@@ -248,7 +265,9 @@ public class ServerInstanceWrapper
else
return null;
}
-
+
+
+ /* ------------------------------------------------------------ */
private void configure(Server server, Dictionary props) throws Exception
{
String jettyConfigurationUrls = (String) props.get(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS);
@@ -256,15 +275,20 @@ public class ServerInstanceWrapper
if (jettyConfigurations == null || jettyConfigurations.isEmpty()) { return; }
Map<String, Object> id_map = new HashMap<String, Object>();
- //TODO need to put in the id of the server being configured
+ //Put in a mapping for the id "Server" and the name of the server as the instance being configured
id_map.put("Server", server);
+ id_map.put((String)props.get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME), server);
+
Map<String, String> properties = new HashMap<String, String>();
Enumeration<Object> en = props.keys();
while (en.hasMoreElements())
{
Object key = en.nextElement();
Object value = props.get(key);
- properties.put(String.valueOf(key), String.valueOf(value));
+ String keyStr = String.valueOf(key);
+ String valStr = String.valueOf(value);
+ properties.put(keyStr, valStr);
+ server.setAttribute(keyStr, valStr);
}
for (URL jettyConfiguration : jettyConfigurations)
@@ -274,6 +298,11 @@ public class ServerInstanceWrapper
{
// Execute a Jetty configuration file
Resource r = Resource.newResource(jettyConfiguration);
+ if (!r.exists())
+ {
+ LOG.warn("File does not exist "+r);
+ continue;
+ }
is = r.getInputStream();
XmlConfiguration config = new XmlConfiguration(is);
config.getIdMap().putAll(id_map);
@@ -316,45 +345,97 @@ public class ServerInstanceWrapper
*
* It is assumed the server has already been configured with the ContextHandlerCollection structure.
*
- * The server must have an instance of OSGiAppProvider. If one is not provided, it is created.
*/
private void init()
{
// Get the context handler
- _ctxtHandler = (ContextHandlerCollection) _server.getChildHandlerByClass(ContextHandlerCollection.class);
+ _ctxtCollection = (ContextHandlerCollection) _server.getChildHandlerByClass(ContextHandlerCollection.class);
- // get a deployerManager
- Collection<DeploymentManager> deployers = _server.getBeans(DeploymentManager.class);
+ if (_ctxtCollection == null)
+ throw new IllegalStateException("ERROR: No ContextHandlerCollection configured in Server");
+
+ List<String> providerClassNames = new ArrayList<String>();
+
+ // get a deployerManager and some providers
+ List<DeploymentManager> deployers = _server.getBeans(DeploymentManager.class);
if (deployers != null && !deployers.isEmpty())
{
- _deploymentManager = deployers.iterator().next();
-
+ _deploymentManager = deployers.get(0);
+
for (AppProvider provider : _deploymentManager.getAppProviders())
{
- if (provider instanceof OSGiAppProvider)
- {
- _provider = (OSGiAppProvider) provider;
- break;
- }
+ providerClassNames.add(provider.getClass().getName());
}
- if (_provider == null)
+ }
+ else
+ {
+ //add some kind of default
+ _deploymentManager = new DeploymentManager();
+ _deploymentManager.setContexts(_ctxtCollection);
+ _server.addBean(_deploymentManager);
+ }
+
+ _deploymentManager.setUseStandardBindings(false);
+ List<AppLifeCycle.Binding> deploymentLifeCycleBindings = new ArrayList<AppLifeCycle.Binding>();
+ deploymentLifeCycleBindings.add(new OSGiDeployer());
+ deploymentLifeCycleBindings.add(new StandardStarter());
+ deploymentLifeCycleBindings.add(new StandardStopper());
+ deploymentLifeCycleBindings.add(new OSGiUndeployer());
+ _deploymentManager.setLifeCycleBindings(deploymentLifeCycleBindings);
+
+ if (!providerClassNames.contains(BundleWebAppProvider.class.getName()))
+ {
+ // create it on the fly with reasonable default values.
+ try
{
- // 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);
+ BundleWebAppProvider webAppProvider = new BundleWebAppProvider(this);
+ _deploymentManager.addAppProvider(webAppProvider);
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+
+ if (!providerClassNames.contains(ServiceWebAppProvider.class.getName()))
+ {
+ // create it on the fly with reasonable default values.
+ try
+ {
+ ServiceWebAppProvider webAppProvider = new ServiceWebAppProvider(this);
+ _deploymentManager.addAppProvider(webAppProvider);
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
}
}
- if (_ctxtHandler == null || _provider == null) throw new IllegalStateException("ERROR: No ContextHandlerCollection or OSGiAppProvider configured");
+ if (!providerClassNames.contains(BundleContextProvider.class.getName()))
+ {
+ try
+ {
+ BundleContextProvider contextProvider = new BundleContextProvider(this);
+ _deploymentManager.addAppProvider(contextProvider);
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+ if (!providerClassNames.contains(ServiceContextProvider.class.getName()))
+ {
+ try
+ {
+ ServiceContextProvider contextProvider = new ServiceContextProvider(this);
+ _deploymentManager.addAppProvider(contextProvider);
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
}
/**
@@ -381,10 +462,6 @@ public class ServerInstanceWrapper
return new File(jettyHome, "/contexts");
}
- File getOSGiContextsHome()
- {
- return _provider.getContextXmlDirAsFile();
- }
/**
* @return the urls in this string.
@@ -398,7 +475,7 @@ public class ServerInstanceWrapper
String tok = tokenizer.nextToken();
try
{
- urls.add(((DefaultFileLocatorHelper) WebBundleDeployerHelper.BUNDLE_FILE_LOCATOR_HELPER).getLocalURL(new URL(tok)));
+ urls.add(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(new URL(tok)));
}
catch (Throwable mfe)
{
@@ -422,7 +499,7 @@ public class ServerInstanceWrapper
try
{
URL url = new URL(tok);
- url = ((DefaultFileLocatorHelper) WebBundleDeployerHelper.BUNDLE_FILE_LOCATOR_HELPER).getFileURL(url);
+ url = BundleFileLocatorHelperFactory.getFactory().getHelper().getFileURL(url);
if (url.getProtocol().equals("file"))
{
Resource res = Resource.newResource(url);
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/BundleFileLocatorHelperFactory.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/BundleFileLocatorHelperFactory.java
new file mode 100644
index 0000000000..197008d8f0
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/BundleFileLocatorHelperFactory.java
@@ -0,0 +1,58 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.internal.webapp;
+
+import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * BundleFileLocatorHelperFactory
+ *
+ * Obtain a helper for locating files based on the bundle.
+ */
+public class BundleFileLocatorHelperFactory
+{
+ private static final Logger LOG = Log.getLogger(BundleFileLocatorHelperFactory.class);
+
+ private static BundleFileLocatorHelperFactory _instance = new BundleFileLocatorHelperFactory();
+
+ private BundleFileLocatorHelperFactory() {}
+
+ public static BundleFileLocatorHelperFactory getFactory()
+ {
+ return _instance;
+ }
+
+ public BundleFileLocatorHelper getHelper()
+ {
+ BundleFileLocatorHelper helper = BundleFileLocatorHelper.DEFAULT;
+ try
+ {
+ //see if a fragment has supplied an alternative
+ helper = (BundleFileLocatorHelper) Class.forName(BundleFileLocatorHelper.CLASS_NAME).newInstance();
+ }
+ catch (Throwable t)
+ {
+ LOG.ignore(t);
+ }
+ return helper;
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/IWebBundleDeployerHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/IWebBundleDeployerHelper.java
index 502d972783..c324c5f676 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/IWebBundleDeployerHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/IWebBundleDeployerHelper.java
@@ -18,6 +18,7 @@
package org.eclipse.jetty.osgi.boot.internal.webapp;
+import org.eclipse.jetty.deploy.ContextDeployer;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java
index 00dea2dfb7..6bd352da2a 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java
@@ -20,19 +20,25 @@ package org.eclipse.jetty.osgi.boot.internal.webapp;
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
+import org.eclipse.jetty.osgi.boot.BundleWebAppProvider;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
+import org.eclipse.jetty.osgi.boot.ServiceProvider;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.IManagedJettyServerRegistry;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
@@ -40,107 +46,83 @@ import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
/**
- * When a {@link ContextHandler} service is activated we look into it and if the
- * corresponding webapp is actually not configured then we go and register it.
- * <p>
- * The idea is to always go through this class when we deploy a new webapp on
- * jetty.
- * </p>
- * <p>
- * We are exposing each web-application as an OSGi service. This lets us update
- * the webapps and stop/start them directly at the OSGi layer. It also give us
- * many ways to declare those services: Declarative Services for example. <br/>
- * It is a bit different from the way the HttpService works where we would have
- * a WebappService and we woud register a webapp onto it. <br/>
- * It does not go against RFC-66 nor does it prevent us from supporting the
- * WebappContainer semantics.
- * </p>
+ * JettyContextHandlerServiceTracker
+ *
+ * When a {@link ContextHandler} is activated as an osgi service we find a jetty deployer
+ * for it. The ContextHandler could be either a WebAppContext or any other derivative of
+ * ContextHandler.
+ *
+ * ContextHandlers and WebApps can also be deployed into jetty without creating them as
+ * osgi services. Instead, they can be deployed via manifest headers inside bundles. See
+ * {@link WebBundleTrackerCustomizer}.
*/
public class JettyContextHandlerServiceTracker implements ServiceListener
{
- private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName());
-
- /** 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;
-
+ private static Logger LOG = Log.getLogger(JettyContextHandlerServiceTracker.class);
+
+ public static final String FILTER = "(objectclass=" + ServiceProvider.class.getName() + ")";
+
+
+ //track all instances of deployers of webapps as bundles
+ ServiceTracker _serviceTracker;
+
+
+
+ /* ------------------------------------------------------------ */
/**
* @param registry
*/
- public JettyContextHandlerServiceTracker(IManagedJettyServerRegistry registry) throws Exception
+ public JettyContextHandlerServiceTracker() throws Exception
{
- _registry = registry;
+ //track all instances of deployers of webapps
+ Bundle myBundle = FrameworkUtil.getBundle(this.getClass());
+ _serviceTracker = new ServiceTracker(myBundle.getBundleContext(), FrameworkUtil.createFilter(FILTER),null);
+ _serviceTracker.open();
}
- public void stop() throws Exception
- {
- if (_scanner != null)
- {
- _scanner.stop();
- }
- // the class that created the server is also in charge of stopping it.
- // nothing to stop in the WebappRegistrationHelper
-
- }
+
+ /* ------------------------------------------------------------ */
/**
- * @param contextHome Parent folder where the context files can override the
- * context files defined in the web bundles: equivalent to the
- * contexts folder in a traditional jetty installation. when
- * null, just do nothing.
+ * @param managedServerName
+ * @return
*/
- protected void setupContextHomeScanner(File contextHome) throws IOException
+ public Map<ServiceReference, ServiceProvider> getDeployers(String managedServerName)
{
- if (contextHome == null) { return; }
- final String osgiContextHomeFolderCanonicalPath = contextHome.getCanonicalPath();
- _scanner = new Scanner();
- _scanner.setRecursive(true);
- _scanner.setReportExistingFilesOnStartup(false);
- _scanner.addListener(new Scanner.DiscreteListener()
+ if (managedServerName == null)
+ managedServerName = OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME;
+
+ Map<ServiceReference, ServiceProvider> candidates = new HashMap<ServiceReference, ServiceProvider>();
+
+ ServiceReference[] references = _serviceTracker.getServiceReferences();
+ if (references != null)
{
- public void fileAdded(String filename) throws Exception
- {
- // 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
+ for (ServiceReference ref:references)
{
- // removing a file does not remove the app:
- // it just goes back to the default embedded in the bundle.
- // well, if there was no default then it does remove it.
- reloadJettyContextHandler(filename, osgiContextHomeFolderCanonicalPath);
+ String name = (String)ref.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
+ if (managedServerName.equalsIgnoreCase(name))
+ {
+ ServiceProvider candidate = (ServiceProvider)_serviceTracker.getService(ref);
+ if (candidate != null)
+ candidates.put(ref, candidate);
+ }
}
- });
-
+ }
+ return candidates;
}
+ /* ------------------------------------------------------------ */
/**
* Receives notification that a service has had a lifecycle change.
*
* @param ev The <code>ServiceEvent</code> object.
*/
+ /**
+ * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
+ */
public void serviceChanged(ServiceEvent ev)
{
ServiceReference sr = ev.getServiceReference();
@@ -149,16 +131,31 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
case ServiceEvent.MODIFIED:
case ServiceEvent.UNREGISTERING:
{
- ContextHandler ctxtHandler = unregisterInIndex(ev.getServiceReference());
- if (ctxtHandler != null && !ctxtHandler.isStopped())
+ BundleContext context = FrameworkUtil.getBundle(JettyBootstrapActivator.class).getBundleContext();
+ ContextHandler contextHandler = (ContextHandler) context.getService(sr);
+
+ //if this was not a service that another of our deployers may have deployed (in which case they will undeploy it)
+ String watermark = (String)sr.getProperty(OSGiWebappConstants.WATERMARK);
+
+ //Get a jetty deployer targetted to the named server instance, or the default one if not named
+ //The individual deployer will decide if it can remove the context or not
+ String serverName = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
+ Map<ServiceReference, ServiceProvider> candidates = getDeployers(serverName);
+ if (candidates != null)
{
- try
+ boolean removed = false;
+ Iterator<Entry<ServiceReference, ServiceProvider>> itor = candidates.entrySet().iterator();
+ while (!removed && itor.hasNext())
{
- getWebBundleDeployerHelp(sr).unregister(ctxtHandler);
- }
- catch (Exception e)
- {
- __logger.warn(e);
+ Entry<ServiceReference, ServiceProvider> e = itor.next();
+ try
+ {
+ removed = e.getValue().serviceRemoved(sr, contextHandler);
+ }
+ catch (Exception x)
+ {
+ LOG.warn("Error undeploying service representing jetty context ", x);
+ }
}
}
}
@@ -181,210 +178,34 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
// is configured elsewhere.
return;
}
- String contextFilePath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
- if (contextHandler instanceof WebAppContext && contextFilePath == null)
- // it could be a web-application that will in fact be configured
- // via a context file.
- // that case is identified by the fact that the contextFilePath
- // is not null
- // in that case we must use the register context methods.
+ String watermark = (String)sr.getProperty(OSGiWebappConstants.WATERMARK);
+ if (watermark != null && !"".equals(watermark))
+ return; //one of our deployers just registered the context as an OSGi service, so we can ignore it
+
+ //Get a jetty deployer targetted to the named server instance, or the default one if not named
+ String serverName = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
+ Map<ServiceReference, ServiceProvider> candidates = getDeployers(serverName);
+ if (candidates != null)
{
- WebAppContext 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)
+ boolean added = false;
+ Iterator<Entry<ServiceReference, ServiceProvider>> itor = candidates.entrySet().iterator();
+ while (!added && itor.hasNext())
{
- String jettyHome = System.getProperty(DefaultJettyAtJettyHomeHelper.SYS_PROP_JETTY_HOME);
- if (jettyHome != null)
+ Entry<ServiceReference, ServiceProvider> e = itor.next();
+ try
{
- 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();
+ added = e.getValue().serviceAdded(sr, contextHandler);
+ if (added && LOG.isDebugEnabled())
+ LOG.debug("Provider "+e.getValue()+" deployed "+contextHandler);
}
- }
- String war = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WAR);
- try
- {
- IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr);
- if (deployerHelper == null)
+ catch (Exception x)
{
-
+ LOG.warn("Error deploying service representing jetty context", x);
}
- 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;
}
- 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;
- }
-
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java
index 77b07a719d..5f7e6443d9 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java
@@ -34,6 +34,7 @@ import java.util.jar.JarFile;
import javax.servlet.http.HttpServlet;
import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelper;
+import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelperFactory;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
@@ -86,12 +87,12 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
* @param contributor The bundle that defines this web-application.
* @throws IOException
*/
- public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor, BundleClassLoaderHelper bundleClassLoaderHelper)
+ public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor)
throws IOException
{
super(parent, context);
_contributor = contributor;
- _osgiBundleClassLoader = bundleClassLoaderHelper.getBundleClassLoader(contributor);
+ _osgiBundleClassLoader = BundleClassLoaderHelperFactory.getFactory().getHelper().getBundleClassLoader(contributor);
}
/**
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java
index db976358dd..b8adc65705 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java
@@ -19,46 +19,70 @@
package org.eclipse.jetty.osgi.boot.internal.webapp;
-import java.net.URL;
-import java.util.Dictionary;
+import java.util.ArrayList;
+import java.util.Collection;
-import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
-import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
+import org.eclipse.jetty.osgi.boot.BundleProvider;
+import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
+import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.BundleTracker;
import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.ServiceTracker;
/**
- * Support bundles that declare the webapp directly through headers in their
- * manifest.
- * <p>
- * Those headers will define a new WebApplication:
- * <ul>
- * <li>Web-ContextPath</li>
- * <li>Jetty-WarFolderPath</li>
- * </ul>
- * </p>
- * <p>
- * Those headers will define a new app started via a jetty-context or a list of
- * them. ',' column is the separator between the various context files.
- * <ul>
- * <li>Jetty-ContextFilePath</li>
- * </ul>
- * </p>
- * And generate a jetty WebAppContext or another ContextHandler then registers
- * it as service. Kind of simpler than declarative services and their xml files.
- * Also avoid having the contributing bundle depend on jetty's package for
- * WebApp.
+ * WebBundleTrackerCustomizer
+ *
+ *
+ * Support bundles that declare a webpp or context directly through headers in their
+ * manifest. They will be deployed to the default jetty Server instance.
+ *
+ * If you wish to deploy a context or webapp to a different jetty Server instance,
+ * register your context/webapp as an osgi service, and set the property OSGiServerConstants.MANAGED_JETTY_SERVER_NAME
+ * with the name of the Server instance you wish to depoy to.
*
* @author hmalphettes
*/
public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
{
private static final Logger LOG = Log.getLogger(WebBundleTrackerCustomizer.class);
+
+ public static Collection<WebappRegistrationCustomizer> JSP_REGISTRATION_HELPERS = new ArrayList<WebappRegistrationCustomizer>();
+ public static final String FILTER = "(&(objectclass=" + BundleProvider.class.getName() + ")"+
+ "("+OSGiServerConstants.MANAGED_JETTY_SERVER_NAME+"="+OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME+"))";
+
+ private ServiceTracker _serviceTracker;
+ private BundleTracker _bundleTracker;
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @throws Exception
+ */
+ public WebBundleTrackerCustomizer ()
+ throws Exception
+ {
+ Bundle myBundle = FrameworkUtil.getBundle(this.getClass());
+
+ //track all instances of deployers of webapps/contexts as bundles
+ _serviceTracker = new ServiceTracker(myBundle.getBundleContext(), FrameworkUtil.createFilter(FILTER),null) {
+ public Object addingService(ServiceReference reference) {
+ Object object = super.addingService(reference);
+ LOG.debug("Deployer registered {}", reference);
+ openBundleTracker();
+ return object;
+ }
+ };
+ _serviceTracker.open();
+ }
+
+
+ /* ------------------------------------------------------------ */
/**
* A bundle is being added to the <code>BundleTracker</code>.
*
@@ -83,8 +107,7 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
{
if (bundle.getState() == Bundle.ACTIVE)
{
- boolean isWebBundle = register(bundle);
- return isWebBundle ? bundle : null;
+ register(bundle);
}
else if (bundle.getState() == Bundle.STOPPING)
{
@@ -98,6 +121,8 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
return null;
}
+
+ /* ------------------------------------------------------------ */
/**
* A bundle tracked by the <code>BundleTracker</code> has been modified.
*
@@ -125,6 +150,8 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
}
}
+
+ /* ------------------------------------------------------------ */
/**
* A bundle tracked by the <code>BundleTracker</code> has been removed.
*
@@ -143,129 +170,81 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
unregister(bundle);
}
+
+ /* ------------------------------------------------------------ */
/**
* @param bundle
* @return true if this bundle in indeed a web-bundle.
*/
private boolean register(Bundle bundle)
{
- Dictionary<?, ?> dic = bundle.getHeaders();
- String warFolderRelativePath = (String) dic.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
- if (warFolderRelativePath != null)
- {
- String contextPath = getWebContextPath(bundle, dic, false);
- if (contextPath == null || !contextPath.startsWith("/"))
- {
- LOG.warn("The manifest header '" + OSGiWebappConstants.JETTY_WAR_FOLDER_PATH
- + ": "
- + warFolderRelativePath
- + "' in the bundle "
- + bundle.getSymbolicName()
- + " is not valid: there is no Web-ContextPath defined in the manifest.");
- 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)
+ if (bundle == null)
+ return false;
+
+ //It might be a bundle that we can deploy to our default jetty server instance
+ boolean deployed = false;
+ Object[] deployers = _serviceTracker.getServices();
+ if (deployers != null)
{
- 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)
+ int i=0;
+ while (!deployed && i<deployers.length)
{
+
+ BundleProvider p = (BundleProvider)deployers[i];
try
{
- JettyBootstrapActivator.registerContext(bundle, path.trim());
+ deployed = p.bundleAdded(bundle);
}
- catch (Throwable e)
+ catch (Exception x)
{
- LOG.warn(e);
+ LOG.warn("Error deploying bundle for jetty context", x);
}
- }
- 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.
+ i++;
}
}
+
+ return deployed;
}
- private String getWebContextPath(Bundle bundle, Dictionary<?, ?> dic, boolean webinfWebxmlExists)
- {
- String rfc66ContextPath = (String) dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
- if (rfc66ContextPath == null)
+ /* ------------------------------------------------------------ */
+ /**
+ * @param bundle
+ */
+ private void unregister(Bundle bundle)
+ {
+ Object[] deployers = _serviceTracker.getServices();
+ boolean undeployed = false;
+ if (deployers != 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)
+ int i=0;
+ while (!undeployed && i<deployers.length)
{
- rfc66ContextPath = rfc66ContextPath.substring(0, lastDot);
+ try
+ {
+ undeployed = ((BundleProvider)deployers[i++]).bundleRemoved(bundle);
+ }
+ catch (Exception x)
+ {
+ LOG.warn("Error undeploying bundle for jetty context", x);
+ }
}
}
- if (!rfc66ContextPath.startsWith("/"))
- {
- rfc66ContextPath = "/" + rfc66ContextPath;
+ }
+
+ public void setAndOpenWebBundleTracker(BundleTracker bundleTracker) {
+ if(_bundleTracker == null) {
+ _bundleTracker = bundleTracker;
+ LOG.debug("Bundle tracker is set");
+ openBundleTracker();
}
- 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.
+ private void openBundleTracker() {
+ if(_bundleTracker != null && _serviceTracker.getServices() != null &&
+ _serviceTracker.getServices().length > 0) {
+ _bundleTracker.open();
+ LOG.debug("Bundle tracker has been opened");
+ }
}
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelperFactory.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelperFactory.java
new file mode 100644
index 0000000000..edcfde0f57
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelperFactory.java
@@ -0,0 +1,61 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.utils;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * BundleClassLoaderHelperFactory
+ *
+ * Get a class loader helper adapted for the particular osgi environment.
+ */
+public class BundleClassLoaderHelperFactory
+{
+ private static final Logger LOG = Log.getLogger(BundleClassLoaderHelperFactory.class);
+
+ private static BundleClassLoaderHelperFactory _instance = new BundleClassLoaderHelperFactory();
+
+ public static BundleClassLoaderHelperFactory getFactory()
+ {
+ return _instance;
+ }
+
+ private BundleClassLoaderHelperFactory()
+ {
+ }
+
+ public BundleClassLoaderHelper getHelper()
+ {
+ //use the default
+ BundleClassLoaderHelper helper = BundleClassLoaderHelper.DEFAULT;
+ try
+ {
+ //if a fragment has not provided their own impl
+ helper = (BundleClassLoaderHelper) Class.forName(BundleClassLoaderHelper.CLASS_NAME).newInstance();
+ }
+ catch (Throwable t)
+ {
+ LOG.ignore(t);
+ }
+
+ return helper;
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java
index ebba3dbd6e..0809b72906 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleFileLocatorHelper.java
@@ -89,5 +89,31 @@ public interface BundleFileLocatorHelper
* @return null or all the entries found for that path.
*/
public Enumeration<URL> findEntries(Bundle bundle, String entryPath);
+
+ /**
+ * Only useful for equinox: on felix we get the file:// or jar:// url
+ * already. Other OSGi implementations have not been tested
+ * <p>
+ * Get a URL to the bundle entry that uses a common protocol (i.e. file:
+ * jar: or http: etc.).
+ * </p>
+ *
+ * @return a URL to the bundle entry that uses a common protocol
+ */
+ public URL getLocalURL(URL url);
+
+ /**
+ * Only useful for equinox: on felix we get the file:// url already. Other
+ * OSGi implementations have not been tested
+ * <p>
+ * Get a URL to the content of the bundle entry that uses the file:
+ * protocol. The content of the bundle entry may be downloaded or extracted
+ * to the local file system in order to create a file: URL.
+ *
+ * @return a URL to the content of the bundle entry that uses the file:
+ * protocol
+ * </p>
+ */
+ public URL getFileURL(URL url);
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/EventSender.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/EventSender.java
new file mode 100644
index 0000000000..13703fa416
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/EventSender.java
@@ -0,0 +1,91 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.utils;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.security.auth.login.FailedLoginException;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+
+public class EventSender
+{
+ //OSGi Event Admin events for webapps
+ public static final String DEPLOYING_EVENT = "org/osgi/service/web/DEPLOYING";
+ public static final String DEPLOYED_EVENT = "org/osgi/service/web/DEPLOYED";
+ public static final String UNDEPLOYING_EVENT = "org/osgi/service/web/UNDEPLOYING";
+ public static final String UNDEPLOYED_EVENT = "org/osgi/service/web/UNDEPLOYED";
+ public static final String FAILED_EVENT = "org/osgi/service/web/FAILED";
+
+
+ private static final EventSender __instance = new EventSender();
+ private Bundle _myBundle;
+ private EventAdmin _eventAdmin;
+
+ private EventSender ()
+ {
+ _myBundle = FrameworkUtil.getBundle(EventSender.class);
+ ServiceReference ref = _myBundle.getBundleContext().getServiceReference(EventAdmin.class.getName());
+ if (ref != null)
+ _eventAdmin = (EventAdmin)_myBundle.getBundleContext().getService(ref);
+ }
+
+
+ public static EventSender getInstance()
+ {
+ return __instance;
+ }
+
+ public void send (String topic, Bundle wab, String contextPath)
+ {
+ if (topic==null || wab==null || contextPath==null)
+ return;
+
+ send(topic, wab, contextPath, null);
+ }
+
+
+ public void send (String topic, Bundle wab, String contextPath, Exception ex)
+ {
+ if (_eventAdmin == null)
+ return;
+
+ Dictionary<String,Object> props = new Hashtable<String,Object>();
+ props.put("bundle.symbolicName", wab.getSymbolicName());
+ props.put("bundle.id", wab.getBundleId());
+ props.put("bundle", wab);
+ props.put("bundle.version", wab.getVersion());
+ props.put("context.path", contextPath);
+ props.put("timestamp", System.currentTimeMillis());
+ props.put("extender.bundle", _myBundle);
+ props.put("extender.bundle.symbolicName", _myBundle.getSymbolicName());
+ props.put("extender.bundle.id", _myBundle.getBundleId());
+ props.put("extender.bundle.version", _myBundle.getVersion());
+
+ if (FAILED_EVENT.equalsIgnoreCase(topic) && ex != null)
+ props.put("exception", ex);
+
+ _eventAdmin.sendEvent(new Event(topic, props));
+ }
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/OSGiClassLoader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/OSGiClassLoader.java
new file mode 100644
index 0000000000..8850f5e639
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/OSGiClassLoader.java
@@ -0,0 +1,221 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.boot.utils;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.osgi.framework.Bundle;
+
+/**
+ * OSGiClassLoader
+ *
+ * Class loader that is aware of a bundle. Similar to WebAppClassLoader from Jetty
+ * and the OSGiWebAppClassLoader, but works without webapps.
+ */
+public class OSGiClassLoader extends URLClassLoader
+{
+ private static final Logger LOG = Log.getLogger(OSGiClassLoader.class);
+
+
+ private Bundle _bundle;
+ private ClassLoader _osgiBundleClassLoader;
+ private boolean _lookInOsgiFirst = true;
+ private ClassLoader _parent;
+
+ /* ------------------------------------------------------------ */
+ public OSGiClassLoader(ClassLoader parent, Bundle bundle)
+ {
+ super(new URL[]{}, parent);
+ _parent = getParent();
+ _bundle = bundle;
+ _osgiBundleClassLoader = BundleClassLoaderHelperFactory.getFactory().getHelper().getBundleClassLoader(_bundle);
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Get a resource from the classloader
+ *
+ * Copied from WebAppClassLoader
+ */
+ public URL getResource(String name)
+ {
+ URL url= null;
+ boolean tried_parent= false;
+
+
+ if (_parent!=null && !_lookInOsgiFirst)
+ {
+ tried_parent= true;
+
+ if (_parent!=null)
+ url= _parent.getResource(name);
+ }
+
+ if (url == null)
+ {
+
+ url = _osgiBundleClassLoader.getResource(name);
+
+ if (url == null && name.startsWith("/"))
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("HACK leading / off " + name);
+
+ url = _osgiBundleClassLoader.getResource(name.substring(1));
+ }
+ }
+
+ if (url == null && !tried_parent)
+ {
+ if (_parent!=null)
+ url= _parent.getResource(name);
+ }
+
+ if (url != null)
+ if (LOG.isDebugEnabled())
+ LOG.debug("getResource("+name+")=" + url);
+
+ return url;
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public Class<?> loadClass(String name) throws ClassNotFoundException
+ {
+ return loadClass(name, false);
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
+ {
+ Class<?> c = findLoadedClass(name);
+ ClassNotFoundException ex= null;
+ boolean tried_parent= false;
+
+ if (c == null && _parent!=null && !_lookInOsgiFirst)
+ {
+ tried_parent= true;
+ try
+ {
+ c= _parent.loadClass(name);
+ if (LOG.isDebugEnabled())
+ LOG.debug("loaded " + c);
+ }
+ catch (ClassNotFoundException e)
+ {
+ ex= e;
+ }
+ }
+
+ if (c == null)
+ {
+ try
+ {
+ c= this.findClass(name);
+ }
+ catch (ClassNotFoundException e)
+ {
+ ex= e;
+ }
+ }
+
+ if (c == null && _parent!=null && !tried_parent)
+ c = _parent.loadClass(name);
+
+ if (c == null)
+ throw ex;
+
+ if (resolve)
+ resolveClass(c);
+
+ if (LOG.isDebugEnabled())
+ LOG.debug("loaded " + c+ " from "+c.getClassLoader());
+
+ return c;
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException
+ {
+ Enumeration<URL> osgiUrls = _osgiBundleClassLoader.getResources(name);
+ Enumeration<URL> urls = super.getResources(name);
+ if (_lookInOsgiFirst)
+ {
+ return Collections.enumeration(toList(osgiUrls, urls));
+ }
+ else
+ {
+ return Collections.enumeration(toList(urls, osgiUrls));
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException
+ {
+ try
+ {
+ return _lookInOsgiFirst ? _osgiBundleClassLoader.loadClass(name) : super.findClass(name);
+ }
+ catch (ClassNotFoundException cne)
+ {
+ try
+ {
+ return _lookInOsgiFirst ? super.findClass(name) : _osgiBundleClassLoader.loadClass(name);
+ }
+ catch (ClassNotFoundException cne2)
+ {
+ throw cne;
+ }
+ }
+ }
+
+
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param e
+ * @param e2
+ * @return
+ */
+ private List<URL> toList(Enumeration<URL> e, Enumeration<URL> e2)
+ {
+ List<URL> list = new ArrayList<URL>();
+ while (e != null && e.hasMoreElements())
+ list.add(e.nextElement());
+ while (e2 != null && e2.hasMoreElements())
+ list.add(e2.nextElement());
+ return list;
+ }
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java
index 951443f062..813bff42fc 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/WebappRegistrationCustomizer.java
@@ -20,7 +20,8 @@ package org.eclipse.jetty.osgi.boot.utils;
import java.net.URL;
-import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
+import org.eclipse.jetty.deploy.DeploymentManager;
+
/**
* Fix various shortcomings with the way jasper parses the tld files.
@@ -55,6 +56,6 @@ public interface WebappRegistrationCustomizer
* @return array of URLs
* @throws Exception
*/
- URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper fileLocator) throws Exception;
+ URL[] getJarsWithTlds(DeploymentManager manager, BundleFileLocatorHelper fileLocator) throws Exception;
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
index b1cfcc4897..d60c6fdfd5 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
@@ -33,10 +33,6 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
{
private static boolean identifiedOsgiImpl = false;
-
- private static Class BundleWiringClass = null;
- private static Method BundleWiringClass_getClassLoader_method = null;
- private static Method BundleClass_adapt_method = null;
private static boolean isEquinox = false;
@@ -45,34 +41,13 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
private static void init(Bundle bundle)
{
identifiedOsgiImpl = true;
-
try
{
- BundleWiringClass = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring");
- if (BundleWiringClass != null)
- {
- BundleWiringClass_getClassLoader_method = BundleWiringClass.getDeclaredMethod("getClassLoader", new Class[] {});
- BundleClass_adapt_method = bundle.getClass().getDeclaredMethod("adapt", new Class[] { Class.class });
- BundleClass_adapt_method.setAccessible(true);
- return;
- }
+ isEquinox = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost") != null;
}
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;
- }
+ isEquinox = false;
}
if (!isEquinox)
{
@@ -95,7 +70,6 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
*/
public ClassLoader getBundleClassLoader(Bundle bundle)
{
- //Older OSGi implementations:
String bundleActivator = (String) bundle.getHeaders().get("Bundle-Activator");
if (bundleActivator == null)
{
@@ -119,22 +93,6 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
{
init(bundle);
}
- //This works for OSGi 4.2 and more recent. Aka version 1.6
- //It is using ava reflection to execute:
- //(BundleClassLoader) bundle.adapt(BundleWiring.class).getClassLoader()
- if (BundleClass_adapt_method != null && BundleWiringClass_getClassLoader_method != null)
- {
- try
- {
- Object bundleWiring = BundleClass_adapt_method.invoke(bundle, BundleWiringClass);
- return (ClassLoader)BundleWiringClass_getClassLoader_method.invoke(bundleWiring, new Object[] {});
- }
- catch (Throwable t)
- {
- t.printStackTrace();
- return null;
- }
- }
if (isEquinox)
{
return internalGetEquinoxBundleClassLoader(bundle);
@@ -177,16 +135,13 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
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.
+ // now get the current module from the bundle.
// and return the private field m_classLoader of ModuleImpl
if (Felix_BundleImpl_m_modules_field == null)
{
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
index 3028aa6c2c..2d31459de1 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
@@ -31,13 +31,13 @@ import java.util.zip.ZipFile;
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.util.URIUtil;
-import org.eclipse.jetty.util.resource.FileResource;
import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.resource.FileResource;
import org.osgi.framework.Bundle;
/**
- * From a bundle to its location on the filesystem.
- * Often assumes the bundle is not a jar.
+ * From a bundle to its location on the filesystem. Assumes the bundle is not a
+ * jar.
*
* @author hmalphettes
*/
@@ -182,16 +182,10 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
File file = new File(location.substring("file:".length()));
return file;
}
- else
- {
- //Resort to introspection on felix:
- return getBundleInstallLocationInFelix(bundle);
- }
}
return null;
}
-
-
+
/**
* Locate a file inside a bundle.
*
@@ -303,7 +297,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
*
* @return a URL to the bundle entry that uses a common protocol
*/
- public static URL getLocalURL(URL url)
+ public URL getLocalURL(URL url)
{
if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
{
@@ -339,7 +333,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
* protocol
* </p>
*/
- public static URL getFileURL(URL url)
+ public URL getFileURL(URL url)
{
if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
{
@@ -362,85 +356,4 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
return url;
}
- // Felix introspection
- private static Method Felix_BundleImpl_getArchive_method;
- private static Method Felix_BundleArchive_getCurrentRevision_method;
- private static Method Felix_BundleRevision_getRevisionRootDir_method;
-
- private static boolean felixIntroSpectionDone = false;
-
- /**
- * Introspection of the implementation classes of Felix-3.x and Felix-4.x.
- * <p>
- * See org.apache.felix.framework.cache
- * In pseudo code:
- * <code>
- * File revRootDir = BundleImpl.getArchive().getCurrentRevision().getRevisionRootDir();
- * return new File(revRootDir, bundle.jar) if it exists?
- * else return revRootDir
- * </p>
- * @param bundle
- * @return The File or null if we failed to find it.
- */
- private static File getBundleInstallLocationInFelix(Bundle bundle)
- {
- if (Felix_BundleImpl_getArchive_method == null) {
- if (felixIntroSpectionDone)
- {
- return null;
- }
- felixIntroSpectionDone = true;
- try
- {
- Felix_BundleImpl_getArchive_method = bundle.getClass().getDeclaredMethod("getArchive", new Class[] {});
- Felix_BundleImpl_getArchive_method.setAccessible(true);
- Object archive = Felix_BundleImpl_getArchive_method.invoke(bundle);
- Class bundleArchiveClass = archive.getClass();
- Felix_BundleArchive_getCurrentRevision_method = bundleArchiveClass.getDeclaredMethod("getCurrentRevision", new Class[] {});
- Felix_BundleArchive_getCurrentRevision_method.setAccessible(true);
- Object revision = Felix_BundleArchive_getCurrentRevision_method.invoke(archive);
- Class bundleRevisionClass = revision.getClass();
- Felix_BundleRevision_getRevisionRootDir_method = bundleRevisionClass.getMethod("getRevisionRootDir", new Class[] {});
- Felix_BundleRevision_getRevisionRootDir_method.setAccessible(true);
- }
- catch (Throwable t)
- {
- //nevermind?
- //t.printStackTrace();
- Felix_BundleImpl_getArchive_method = null;
- return null;
- }
- }
- if (Felix_BundleImpl_getArchive_method != null)
- {
- try
- {
- Object archive = Felix_BundleImpl_getArchive_method.invoke(bundle);
- Object revision = Felix_BundleArchive_getCurrentRevision_method.invoke(archive);
- File revRootDir = (File)Felix_BundleRevision_getRevisionRootDir_method.invoke(revision);
- //System.err.println("Got the archive revision root dir " + revRootDir.getAbsolutePath());
- File bundleJar = new File(revRootDir, "bundle.jar");
- if (bundleJar.exists())
- {
- //bundle.jar is hardcoded in org.apache.felix.framework.cache.JarRevision
- //when it is not a bundle.jar, then the bundle location starts with 'file:' and we have already
- //taken care if that scheme earlier.
- return bundleJar;
- }
- else //sanity check?: if (new File(revRootDir, "META-INF/MANIFEST.MF").exists())
- {
- //this is a DirectoryRevision
- return revRootDir;
- }
- }
- catch (Throwable t)
- {
- //best effort: nevermind
- //t.printStackTrace();
- }
- }
- return null;
- }
-// -- end Felix introspection
-
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnectorListener.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnectorListener.java
new file mode 100644
index 0000000000..28cba8b997
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnectorListener.java
@@ -0,0 +1,268 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.nested;
+
+import java.lang.reflect.Method;
+
+import javax.servlet.http.HttpServlet;
+
+import org.eclipse.jetty.nested.NestedConnector;
+import org.eclipse.jetty.util.component.AbstractLifeCycle.AbstractLifeCycleListener;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ * Listens to the start and stop of the NestedConnector to register and
+ * unregister the NestedConnector with the BridgeServlet.
+ * <p>
+ * All interactions with the BridgeServlet are done via introspection to avoid
+ * depending on it directly. The BridgeServlet lives in the bootstrap-webapp;
+ * not inside equinox.
+ * </p>
+ */
+public class NestedConnectorListener extends AbstractLifeCycleListener
+{
+
+ /**
+ * Name of the BridgeServlet class. By default
+ * org.eclipse.equinox.servletbridge.BridgeServlet
+ */
+ private String bridgeServletClassName = "org.eclipse.equinox.servletbridge.BridgeServlet";
+
+ /**
+ * Name of the static method on the BridgeServlet class to register the
+ * servlet delegate. By default 'registerServletDelegate'
+ */
+ private String registerServletDelegateMethodName = "registerServletDelegate";
+
+ /**
+ * Name of the static method on the BridgeServlet class to register the
+ * servlet delegate. By default 'unregisterServletDelegate'
+ */
+ private String unregisterServletDelegateMethodName = "unregisterServletDelegate";
+
+ /**
+ * servlet that wraps this NestedConnector and uses the NestedConnector to
+ * service the requests.
+ */
+ private NestedConnectorServletDelegate _servletDelegate;
+
+ /**
+ * The NestedConnector listened to.
+ */
+ private NestedConnector nestedConnector;
+
+ /**
+ * @param bridgeServletClassName Name of the class that is the
+ * BridgeServlet. By default
+ * org.eclipse.equinox.servletbridge.BridgeServlet
+ */
+ public void setBridgeServletClassName(String bridgeServletClassName)
+ {
+ this.bridgeServletClassName = bridgeServletClassName;
+ }
+
+ public String getBridgeServletClassName()
+ {
+ return this.bridgeServletClassName;
+ }
+
+ public String getRegisterServletDelegateMethodName()
+ {
+ return this.registerServletDelegateMethodName;
+ }
+
+ public String getUnregisterServletDelegateMethodName()
+ {
+ return this.unregisterServletDelegateMethodName;
+ }
+
+ /**
+ * @param registerServletDelegateMethodName Name of the static method on the
+ * BridgeServlet class to register the servlet delegate.
+ */
+ public void setRegisterServletDelegateMethodName(String registerServletDelegateMethodName)
+ {
+ this.registerServletDelegateMethodName = registerServletDelegateMethodName;
+ }
+
+ /**
+ * @param unregisterServletDelegateMethodName Name of the static method on
+ * the BridgeServlet class to unregister the servlet delegate.
+ */
+ public void setUnregisterServletDelegateMethodName(String unregisterServletDelegateMethodName)
+ {
+ this.unregisterServletDelegateMethodName = unregisterServletDelegateMethodName;
+ }
+
+ /**
+ * @param nestedConnector The NestedConnector that we are listening to here.
+ */
+ public void setNestedConnector(NestedConnector nestedConnector)
+ {
+ this.nestedConnector = nestedConnector;
+ }
+
+ /**
+ * @return The NestedConnector that we are listening to here.
+ */
+ public NestedConnector getNestedConnector()
+ {
+ return this.nestedConnector;
+ }
+
+ @Override
+ public void lifeCycleStarted(LifeCycle event)
+ {
+ try
+ {
+ registerWithBridgeServlet();
+ }
+ catch (Exception e)
+ {
+ if (e instanceof RuntimeException) { throw (RuntimeException) e; }
+ throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", e);
+ }
+ }
+
+ @Override
+ public void lifeCycleStopping(LifeCycle event)
+ {
+ try
+ {
+ unregisterWithBridgeServlet();
+ }
+ catch (Exception e)
+ {
+ if (e instanceof RuntimeException) { throw (RuntimeException) e; }
+ throw new RuntimeException("Unable to unregister the servlet delegate into the BridgeServlet.", e);
+ }
+ }
+
+ /**
+ * Hook into the BridgeServlet
+ */
+ protected void registerWithBridgeServlet() throws Exception
+ {
+ _servletDelegate = new NestedConnectorServletDelegate(getNestedConnector());
+ try
+ {
+ invokeStaticMethod(getBridgeServletClassName(), getRegisterServletDelegateMethodName(), new Class[] { HttpServlet.class }, _servletDelegate);
+ }
+ catch (Throwable t)
+ {
+ _servletDelegate.destroy();
+ _servletDelegate = null;
+ if (t instanceof Exception) { throw (Exception) t; }
+ throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", t);
+ }
+ }
+
+ /**
+ * Unhook into the BridgeServlet
+ */
+ protected void unregisterWithBridgeServlet() throws Exception
+ {
+ if (_servletDelegate != null)
+ {
+ try
+ {
+ invokeStaticMethod(getBridgeServletClassName(), getUnregisterServletDelegateMethodName(), new Class[] { HttpServlet.class }, _servletDelegate);
+ }
+ catch (Throwable t)
+ {
+ if (t instanceof Exception) { throw (Exception) t; }
+ throw new RuntimeException("Unable to unregister the servlet delegate from the BridgeServlet.", t);
+ }
+ finally
+ {
+ _servletDelegate.destroy();
+ _servletDelegate = null;
+ }
+ }
+ }
+
+ /**
+ *
+ * @param clName
+ * @param methName
+ * @param argType
+ * @throws Exception
+ */
+ private static void invokeStaticMethod(String clName, String methName, Class[] argType, Object... args) throws Exception
+ {
+ Method m = getMethod(clName, methName, argType);
+ m.invoke(null, args);
+ }
+
+ /**
+ *
+ * @param clName Class that belongs to the parent classloader of the OSGi
+ * framework.
+ * @param methName Name of the method to find.
+ * @param argType Argument types of the method to find.
+ * @throws Exception
+ */
+ private static Method getMethod(String clName, String methName, Class... argType) throws Exception
+ {
+ Class bridgeServletClass = FrameworkUtil.class.getClassLoader().loadClass(clName);
+ return getMethod(bridgeServletClass, methName, argType);
+ }
+
+ private static Method getMethod(Class cl, String methName, Class... argType) throws Exception
+ {
+ Method meth = null;
+ try
+ {
+ meth = cl.getMethod(methName, argType);
+ return meth;
+ }
+ catch (Exception e)
+ {
+ for (Method m : cl.getMethods())
+ {
+ if (m.getName().equals(methName) && m.getParameterTypes().length == argType.length)
+ {
+ int i = 0;
+ for (Class p : m.getParameterTypes())
+ {
+ Class ap = argType[i];
+ if (p.getName().equals(ap.getName()) && !p.equals(ap))
+ {
+ throw new IllegalStateException("The method \"" + m.toGenericString()
+ + "\" was found. but the parameter class "
+ + p.getName()
+ + " is not the same "
+ + " inside OSGi classloader ("
+ + ap.getClassLoader()
+ + ") and inside the "
+ + cl.getName()
+ + " classloader ("
+ + p.getClassLoader()
+ + ")."
+ + " Are the ExtensionBundles correctly defined?");
+
+ }
+ }
+ }
+ }
+ throw e;
+ }
+ }
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnectorServletDelegate.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnectorServletDelegate.java
new file mode 100644
index 0000000000..27b5b7a440
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnectorServletDelegate.java
@@ -0,0 +1,51 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.osgi.nested;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServlet;
+
+import org.eclipse.jetty.nested.NestedConnector;
+
+/**
+ * Wraps a NestedConnector into a servlet that can be plugged into
+ * BridgeServlet#registerServletDelegate
+ */
+public class NestedConnectorServletDelegate extends HttpServlet
+{
+ private static final long serialVersionUID = 1L;
+
+ private final NestedConnector _nestedConnector;
+
+ public NestedConnectorServletDelegate(NestedConnector nestedConnector)
+ {
+ _nestedConnector = nestedConnector;
+ }
+
+ @Override
+ public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
+ {
+ _nestedConnector.service(req, res);
+ }
+
+}

Back to the top