From b2998c2c4d4771eb01daae5d82d35872959551a7 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Mon, 14 May 2012 18:46:21 +0200 Subject: [PATCH 01/26] Refactoring --- ...gableWebAppRegistrationCustomizerImpl.java | 32 +- .../WebappRegistrationCustomizerImpl.java | 13 +- .../osgi/boot/jsp/FragmentActivator.java | 21 +- .../boot/jsp/TagLibOSGiConfiguration.java | 3 +- .../jettyhome/etc/jetty-deployer.xml | 13 +- .../osgi/boot/BundleContextProvider.java | 78 ++ .../jetty/osgi/boot/BundleWebAppProvider.java | 763 +++++++++++++++ .../osgi/boot/JettyBootstrapActivator.java | 8 +- .../osgi/boot/OSGiMetaInfConfiguration.java | 98 ++ .../osgi/boot/OSGiWebInfConfiguration.java | 255 +++++ .../jetty/osgi/boot/OSGiWebappConstants.java | 42 +- .../DefaultJettyAtJettyHomeHelper.java | 3 +- .../serverfactory/ServerInstanceWrapper.java | 61 +- .../BundleFileLocatorHelperFactory.java | 41 + .../JettyContextHandlerServiceTracker.java | 75 +- .../webapp/OSGiWebappClassLoader.java | 5 +- .../webapp/WebBundleDeployerHelper.java | 904 ------------------ .../webapp/WebBundleTrackerCustomizer.java | 162 +--- .../utils/BundleClassLoaderHelperFactory.java | 44 + .../boot/utils/BundleFileLocatorHelper.java | 26 + .../utils/WebappRegistrationCustomizer.java | 3 +- .../internal/DefaultFileLocatorHelper.java | 4 +- jetty-osgi/pom.xml | 1 + jetty-osgi/test-jetty-osgi-webapp/pom.xml | 129 +++ .../main/java/com/acme/osgi/Activator.java | 60 ++ .../src/main/resources/index.html | 6 + jetty-osgi/test-jetty-osgi/pom.xml | 15 + .../src/test/config/etc/jetty-deployer.xml | 43 + .../src/test/config/etc/jetty-selector.xml | 26 + .../src/test/config/etc/jetty.xml | 72 ++ .../osgi/boot/TestJettyOSGiBootCore.java | 2 +- .../TestJettyOSGiBootWebAppAsService.java | 166 ++++ .../osgi/boot/TestJettyOSGiBootWithJsp.java | 15 +- 33 files changed, 2078 insertions(+), 1111 deletions(-) create mode 100644 jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java create mode 100644 jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java create mode 100644 jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiMetaInfConfiguration.java create mode 100644 jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java create mode 100644 jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/BundleFileLocatorHelperFactory.java delete mode 100644 jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java create mode 100644 jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelperFactory.java create mode 100644 jetty-osgi/test-jetty-osgi-webapp/pom.xml create mode 100644 jetty-osgi/test-jetty-osgi-webapp/src/main/java/com/acme/osgi/Activator.java create mode 100644 jetty-osgi/test-jetty-osgi-webapp/src/main/resources/index.html create mode 100644 jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-deployer.xml create mode 100644 jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-selector.xml create mode 100644 jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty.xml create mode 100644 jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootWebAppAsService.java diff --git a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/PluggableWebAppRegistrationCustomizerImpl.java b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/PluggableWebAppRegistrationCustomizerImpl.java index 79854f2c99f..6ba3d1c5bd5 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/PluggableWebAppRegistrationCustomizerImpl.java +++ b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/PluggableWebAppRegistrationCustomizerImpl.java @@ -19,9 +19,13 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.StringTokenizer; +import java.util.regex.Pattern; +import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.osgi.boot.OSGiAppProvider; +import org.eclipse.jetty.osgi.boot.OSGiWebInfConfiguration; import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper; import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer; import org.osgi.framework.Bundle; @@ -56,10 +60,10 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra * @param provider * @return */ - private static Collection getTldBundles(OSGiAppProvider provider) + private static Collection getTldBundles(DeploymentManager deploymentManager) { String sysprop = System.getProperty(SYS_PROP_TLD_BUNDLES); - String att = (String) provider.getTldBundles(); + String att = (String) deploymentManager.getContextAttribute(OSGiWebInfConfiguration.CONTAINER_BUNDLE_PATTERN); if (sysprop == null && att == null) { return Collections.emptySet(); } if (att == null) { @@ -83,9 +87,8 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra * @return The location of the jars that contain tld files. Jasper will * discover them. */ - public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception + public URL[] getJarsWithTlds(DeploymentManager deploymentManager, BundleFileLocatorHelper locatorHelper) throws Exception { - List urls = new ArrayList(); // naive way of finding those bundles. // lots of assumptions: for example we assume a single version of each // bundle that would contain tld files. @@ -96,13 +99,24 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra // and mirroring those in the MANIFEST.MF Bundle[] bundles = FrameworkUtil.getBundle(PluggableWebAppRegistrationCustomizerImpl.class).getBundleContext().getBundles(); - Collection tldbundles = getTldBundles(provider); + HashSet urls = new HashSet(); + String tmp = System.getProperty(SYS_PROP_TLD_BUNDLES); //comma separated exact names + List sysNames = new ArrayList(); + if (tmp != null) + { + StringTokenizer tokenizer = new StringTokenizer(tmp, ", \n\r\t", false); + while (tokenizer.hasMoreTokens()) + sysNames.add(tokenizer.nextToken()); + } + tmp = (String) deploymentManager.getContextAttribute(OSGiWebInfConfiguration.CONTAINER_BUNDLE_PATTERN); //bundle name patterns + Pattern pattern = (tmp==null? null : Pattern.compile(tmp)); for (Bundle bundle : bundles) { - if (tldbundles.contains(bundle.getSymbolicName())) - { + if (sysNames.contains(bundle.getSymbolicName())) + registerTldBundle(locatorHelper, bundle, urls); + + if (pattern != null && pattern.matcher(bundle.getSymbolicName()).matches()) registerTldBundle(locatorHelper, bundle, urls); - } } return urls.toArray(new URL[urls.size()]); @@ -134,7 +148,7 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra * @param urls * @throws Exception */ - private void registerTldBundle(BundleFileLocatorHelper locatorHelper, Bundle bundle, List urls) throws Exception + private void registerTldBundle(BundleFileLocatorHelper locatorHelper, Bundle bundle, Set urls) throws Exception { File jasperLocation = locatorHelper.getBundleInstallLocation(bundle); if (jasperLocation.isDirectory()) diff --git a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java index 44718bb37e3..4fdf8379bc9 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java +++ b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java @@ -25,10 +25,13 @@ import javax.servlet.jsp.JspFactory; import org.apache.jasper.Constants; import org.apache.jasper.compiler.Localizer; import org.apache.jasper.xmlparser.ParserUtils; +import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator; import org.eclipse.jetty.osgi.boot.OSGiAppProvider; import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper; import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; import org.xml.sax.EntityResolver; @@ -46,6 +49,8 @@ import org.xml.sax.SAXException; */ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCustomizer { + private static final Logger LOG = Log.getLogger(WebappRegistrationCustomizerImpl.class); + /** * Default name of a class that belongs to the jstl bundle. From that class @@ -85,8 +90,7 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto } catch (Exception e) { - System.err.println("Unable to locate the JspServlet: jsp support unavailable."); - e.printStackTrace(); + LOG.warn("Unable to locate the JspServlet: jsp support unavailable.", e); return; } try @@ -106,8 +110,7 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto } catch (Exception e) { - System.err.println("Unable to set the JspFactory: jsp support incomplete."); - e.printStackTrace(); + LOG.warn("Unable to set the JspFactory: jsp support incomplete.", e); } } @@ -129,7 +132,7 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto * @return array of URLs * @throws Exception */ - public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception + public URL[] getJarsWithTlds(DeploymentManager deployer, BundleFileLocatorHelper locatorHelper) throws Exception { HashSet> classesToAddToTheTldBundles = new HashSet>(); diff --git a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/FragmentActivator.java b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/FragmentActivator.java index 3f8f2874e65..1b1da582efd 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/FragmentActivator.java +++ b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/FragmentActivator.java @@ -12,7 +12,9 @@ // ======================================================================== package org.eclipse.jetty.osgi.boot.jsp; -import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleDeployerHelper; + +import org.eclipse.jetty.osgi.boot.BundleWebAppProvider; +import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleTrackerCustomizer; import org.eclipse.jetty.osgi.boot.jasper.PluggableWebAppRegistrationCustomizerImpl; import org.eclipse.jetty.osgi.boot.jasper.WebappRegistrationCustomizerImpl; import org.osgi.framework.BundleActivator; @@ -24,7 +26,7 @@ import org.osgi.framework.BundleContext; * called back by the host bundle. *

* It must be placed in the org.eclipse.jetty.osgi.boot.jsp package: this is - * because org.eclipse.jetty.osgi.boot.jsp is the sympbolic-name of this + * because org.eclipse.jetty.osgi.boot.jsp is the symbolic-name of this * fragment. From that name, the PackageadminTracker will call this class. IN a * different package it won't be called. *

@@ -37,8 +39,11 @@ public class FragmentActivator implements BundleActivator public void start(BundleContext context) throws Exception { System.setProperty("org.apache.jasper.compiler.disablejsr199", Boolean.TRUE.toString()); - WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS.add(new WebappRegistrationCustomizerImpl()); - WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS.add(new PluggableWebAppRegistrationCustomizerImpl()); + WebBundleTrackerCustomizer.JSP_REGISTRATION_HELPERS.add(new WebappRegistrationCustomizerImpl()); + WebBundleTrackerCustomizer.JSP_REGISTRATION_HELPERS.add(new PluggableWebAppRegistrationCustomizerImpl()); + //Put in the support for the tag libs + addTagLibSupport(); + } /** @@ -48,4 +53,12 @@ public class FragmentActivator implements BundleActivator { } + + public void addTagLibSupport () + { + String[] defaultConfigurations = new String[BundleWebAppProvider.getDefaultConfigurations().length+1]; + System.arraycopy(BundleWebAppProvider.getDefaultConfigurations(), 0, defaultConfigurations, 0, BundleWebAppProvider.getDefaultConfigurations().length); + defaultConfigurations[defaultConfigurations.length-1] = "org.eclipse.jetty.osgi.boot.jsp.TagLibOSGiConfiguration"; + BundleWebAppProvider.setDefaultConfigurations(defaultConfigurations); + } } diff --git a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/TagLibOSGiConfiguration.java b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/TagLibOSGiConfiguration.java index ad31de22e7f..0859da3d214 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/TagLibOSGiConfiguration.java +++ b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/TagLibOSGiConfiguration.java @@ -19,6 +19,7 @@ import java.util.Enumeration; import java.util.LinkedHashSet; import org.eclipse.jetty.osgi.boot.OSGiWebappConstants; +import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory; import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -108,7 +109,7 @@ public class TagLibOSGiConfiguration extends TagLibConfiguration { atLeastOneTldFound = true; URL oriUrl = en.nextElement(); - URL url = DefaultFileLocatorHelper.getLocalURL(oriUrl); + URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(oriUrl); Resource tldResource; try { diff --git a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-deployer.xml b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-deployer.xml index f05b31c8e16..ac352765627 100644 --- a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-deployer.xml +++ b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-deployer.xml @@ -17,22 +17,25 @@ .*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$ - - - + + + + - + 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 00000000000..e5da466f731 --- /dev/null +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleContextProvider.java @@ -0,0 +1,78 @@ +package org.eclipse.jetty.osgi.boot; + +import java.util.HashMap; +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.server.handler.ContextHandler; +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.osgi.framework.Bundle; + +public class BundleContextProvider extends AbstractLifeCycle implements AppProvider +{ + private DeploymentManager _deploymentManager; + + private Map _appMap = new HashMap(); + + + /* ------------------------------------------------------------ */ + /** + * BundleApp + * + * + */ + public class BundleApp extends App + { + private String _contextFile; + private Bundle _bundle; + + public BundleApp(DeploymentManager manager, AppProvider provider, String originId, Bundle bundle, String contextFile) + { + super(manager, provider, originId); + _bundle = bundle; + _contextFile = contextFile; + } + + + public String getContextFile () + { + return _contextFile; + } + } + + + /* ------------------------------------------------------------ */ + public void setDeploymentManager(DeploymentManager deploymentManager) + { + _deploymentManager = deploymentManager; + } + + + /* ------------------------------------------------------------ */ + public ContextHandler createContextHandler(App app) throws Exception + { + //apply the contextFile, creating the ContextHandler, the DeploymentManager will register it in the ContextHandlerCollection + return null; + } + + /* ------------------------------------------------------------ */ + public void bundleAdded (Bundle bundle, String contextFiles) + { + if (contextFiles == null) + return; + + //bundle defines OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH header, + //a comma separated list of context xml files that each define a ContextHandler (could be WebAppContexts) + String[] tmp = contextFiles.split(",;"); + for (String contextFile : tmp) + { + String originId = bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + "-"+contextFile; + BundleApp app = new BundleApp(_deploymentManager, this, originId, bundle, contextFile); + _appMap.put(originId,app); + _deploymentManager.addApp(app); + } + } + +} 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 00000000000..5aeba514666 --- /dev/null +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java @@ -0,0 +1,763 @@ +package org.eclipse.jetty.osgi.boot; + +import java.io.File; +import java.net.URL; +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.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; + + + +/** + * BundleWebAppProvider + * + * + */ +public class BundleWebAppProvider extends AbstractLifeCycle implements AppProvider +{ + private static final Logger LOG = Log.getLogger(BundleWebAppProvider.class); + + public static final String WATERMARK = "o.e.j.o.b.BWAP"; //indicates this class created the webapp + + 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" + }; + + private DeploymentManager _deploymentManager; + + private Map _appMap = new HashMap(); + + private Map _bundleMap = new HashMap(); + + private boolean _parentLoaderPriority; + + private String _defaultsDescriptor; + + private boolean _extractWars = true; //See WebAppContext.extractWars + + private String _tldBundles; + + private String[] _configurationClasses; + + private ServerInstanceWrapper _serverWrapper; + + private ServiceRegistration _serviceReg; + + + public static void setDefaultConfigurations (String[] defaultConfigs) + { + __defaultConfigurations = defaultConfigs; + } + + public static String[] getDefaultConfigurations () + { + return __defaultConfigurations; + } + + + /* ------------------------------------------------------------ */ + /** + * BundleApp + * + * + */ + public class BundleApp extends App + { + private Bundle _bundle; + private String _contextPath; + private String _webAppPath; + private WebAppContext _webApp; + private Dictionary _properties; + + public BundleApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId) + { + super(manager, provider, originId); + _properties = bundle.getHeaders(); + _bundle = bundle; + } + + public BundleApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId) + { + super(manager, provider, originId); + _properties = properties; + _bundle = bundle; + } + + public void setWebAppContext (WebAppContext webApp) + { + _webApp = webApp; + } + + + public Bundle getBundle() + { + return _bundle; + } + + 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 WebAppContext getWebAppContext() + 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(WATERMARK, WATERMARK); + return webApp; + } + + + public void configureWebApp() + throws Exception + { + _webApp.setContextPath(_contextPath); + + 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 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); + + for (int i=0;i<__defaultConfigurations.length;i++) + System.err.println(__defaultConfigurations[i]); + + 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()); + + // 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 BundleWebAppProvider (ServerInstanceWrapper wrapper) + { + _serverWrapper = wrapper; + } + + + + + /* ------------------------------------------------------------ */ + protected void doStart() throws Exception + { + //register as an osgi service, advertising the name of the jetty Server instance we are related to + Dictionary properties = new Hashtable(); + properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, _serverWrapper.getManagedServerName()); + _serviceReg = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(this.getClass().getName(), this, properties); + super.doStart(); + } + + /* ------------------------------------------------------------ */ + @Override + protected void doStop() throws Exception + { + //unregister ourselves + if (_serviceReg != null) + { + try + { + _serviceReg.unregister(); + } + catch (Exception e) + { + LOG.warn(e); + } + } + super.doStop(); + } + + + /* ------------------------------------------------------------ */ + public DeploymentManager getDeploymentManager() + { + return _deploymentManager; + } + + /* ------------------------------------------------------------ */ + public void setDeploymentManager(DeploymentManager deploymentManager) + { + _deploymentManager = deploymentManager; + } + + /* ------------------------------------------------------------ */ + /** + * 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 ContextHandler createContextHandler(App app) throws Exception + { + if (app == null) + return null; + if (!(app instanceof BundleApp)) + throw new IllegalStateException(app+" is not a BundleApp"); + + //Create a WebAppContext suitable to deploy in OSGi + return ((BundleApp)app).getWebAppContext(); + } + + + + /* ------------------------------------------------------------ */ + /** + * A bundle has been added that could be a webapp + * @param bundle + */ + public boolean bundleAdded (Bundle bundle) + { + if (bundle == null) + return false; + + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(_serverWrapper.getParentClassLoaderForWebapps()); + 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); + String contextPath = getContextPath(bundle); + String originId = getOriginId(bundle, base); + + BundleApp app = new BundleApp(_deploymentManager, this, bundle, originId); + app.setWebAppPath(base); + app.setContextPath(contextPath); + _appMap.put(originId,app); + _bundleMap.put(bundle, app); + _deploymentManager.addApp(app); + + return true; + } + + + //does the bundle have a WEB-INF/web.xml + if (bundle.getEntry("/WEB-INF/web.xml") != null) + { + String base = "."; + String contextPath = getContextPath(bundle); + String originId = getOriginId(bundle, base); + + BundleApp app = new BundleApp(_deploymentManager, this, bundle, originId); + app.setContextPath(contextPath); + app.setWebAppPath(base); + _appMap.put(originId,app); + _bundleMap.put(bundle, app); + _deploymentManager.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 = "."; + String contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); + String originId = getOriginId(bundle,base); + + BundleApp app = new BundleApp(_deploymentManager, this, bundle, originId); + app.setContextPath(contextPath); + app.setWebAppPath(base); + _appMap.put(originId,app); + _bundleMap.put(bundle, app); + _deploymentManager.addApp(app); + return true; + } + + return false; + } + 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) + { + App app = _bundleMap.remove(bundle); + if (app != null) + { + _appMap.remove(app.getOriginId()); + _deploymentManager.removeApp(app); + return true; + } + return false; + } + + + /* ------------------------------------------------------------ */ + /** + * A webapp that was deployed as an osgi service has been added, + * and we want to deploy it. + * + * @param webApp + */ + public void serviceAdded (ServiceReference serviceRef, WebAppContext webApp) + { + Dictionary properties = new Hashtable(); + + String contextPath = (String)serviceRef.getProperty(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); + if (contextPath == null) + contextPath = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH); + + String base = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH); + if (base == null) + base = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_WAR); + + 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(_serverWrapper.getParentClassLoaderForWebapps()); + try + { + String originId = getOriginId(serviceRef.getBundle(), base); + BundleApp app = new BundleApp(_deploymentManager, this, serviceRef.getBundle(), properties, originId); + app.setContextPath(contextPath); + app.setWebAppPath(base); + app.setWebAppContext(webApp); //set the pre=made webapp instance + _appMap.put(originId,app); + _bundleMap.put(serviceRef.getBundle(), app); + _deploymentManager.addApp(app); + } + finally + { + Thread.currentThread().setContextClassLoader(cl); + } + } + + + + /* ------------------------------------------------------------ */ + /** + * @param webApp + */ + public boolean serviceRemoved (ServiceReference serviceRef, WebAppContext webApp) + { + App app = _bundleMap.remove(serviceRef.getBundle()); + if (app != null) + { + _appMap.remove(app.getOriginId()); + _deploymentManager.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; + } + + + /* ------------------------------------------------------------ */ + private 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/JettyBootstrapActivator.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java index 96b23dadc82..bf1d7176f5a 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 @@ -33,7 +33,7 @@ import org.osgi.framework.BundleException; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceRegistration; import org.osgi.util.tracker.BundleTracker; - +//mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot-jsp" ).versionAsInProject().start(), /** * Bootstrap jetty and publish a default Server instance as an OSGi service. * @@ -62,8 +62,6 @@ public class JettyBootstrapActivator implements BundleActivator private ServiceRegistration _registeredServer; - private Server _server; - private JettyContextHandlerServiceTracker _jettyContextHandlerTracker; private PackageAdminServiceTracker _packageAdminServiceTracker; @@ -159,10 +157,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/OSGiMetaInfConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiMetaInfConfiguration.java new file mode 100644 index 00000000000..4fe8e63050e --- /dev/null +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiMetaInfConfiguration.java @@ -0,0 +1,98 @@ +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 frags = (List) context.getAttribute(METAINF_FRAGMENTS); + List resfrags = (List) context.getAttribute(METAINF_RESOURCES); + List tldfrags = (List) 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 resEnum = frag.findEntries("/META-INF/resources", "*", true); + Enumeration 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(); + 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(); + 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(); + 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/OSGiWebInfConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java new file mode 100644 index 00000000000..7505e5a2e11 --- /dev/null +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java @@ -0,0 +1,255 @@ +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: + *
    + *
  1. SystemProperty SYS_PROP_TLD_BUNDLES
  2. + *
  3. DeployerManager.setContextAttribute CONTAINER_BUNDLE_PATTERN
  4. + *
+ * + * 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 names = new ArrayList(); + 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 matchingResources = new HashSet(); + 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 findJars (WebAppContext context) + throws Exception + { + List mergedResources = new ArrayList(); + //get jars from WEB-INF/lib if there are any + List 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 patchResourcesPath = new TreeMap(); + TreeMap appendedResourcesPath = new TreeMap(); + + 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(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 getBundleAsResource(Bundle bundle) + throws Exception + { + List resources = new ArrayList(); + + 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 1ba8746605a..1bd0030117d 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 @@ -55,32 +55,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. */ 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 a9d2ce59667..0aea68c2b2e 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 @@ -177,8 +177,6 @@ public class DefaultJettyAtJettyHomeHelper //register the Server instance as an OSGi service. bundleContext.registerService(Server.class.getName(), server, properties); - // hookNestedConnectorToBridgeServlet(server); - } /** @@ -226,6 +224,7 @@ public class DefaultJettyAtJettyHomeHelper */ private static String getJettyConfigurationURLs(Bundle configurationBundle) { + System.err.println("GETTING JETTY PROPS FROM BUNDLE: "+configurationBundle.getSymbolicName()); String files = System.getProperty(SYS_PROP_JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES); StringTokenizer tokenizer = new StringTokenizer(files, ";,", false); 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 952a768a995..eb0051a8de3 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 @@ -26,14 +26,14 @@ import java.util.StringTokenizer; import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; +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.OSGiServerConstants; 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; @@ -78,9 +78,7 @@ public class ServerInstanceWrapper private DeploymentManager _deploymentManager; - private OSGiAppProvider _provider; - - private WebBundleDeployerHelper _webBundleDeployerHelper; + private BundleWebAppProvider _provider; public ServerInstanceWrapper(String managedServerName) { @@ -114,20 +112,11 @@ public class ServerInstanceWrapper /** * @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 @@ -151,6 +140,8 @@ public class ServerInstanceWrapper List shared = sharedURLs != null ? extractFiles(sharedURLs) : null; libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader(shared, null, server, JettyBootstrapActivator.class.getClassLoader()); + System.err.println("LibExtClassLoader = "+libExtClassLoader); + Thread.currentThread().setContextClassLoader(libExtClassLoader); configure(server, props); @@ -162,9 +153,11 @@ public class ServerInstanceWrapper URL[] jarsWithTlds = getJarsWithTlds(); _commonParentClassLoaderForWebapps = jarsWithTlds == null ? libExtClassLoader : new TldLocatableURLClassloader(libExtClassLoader, jarsWithTlds); + + System.err.println("common classloader = "+_commonParentClassLoaderForWebapps); server.start(); - _webBundleDeployerHelper = new WebBundleDeployerHelper(this); + //_webBundleDeployerHelper = new WebBundleDeployerHelper(this); } catch (Exception e) { @@ -226,11 +219,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 res = new ArrayList(); - 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); @@ -324,9 +325,11 @@ public class ServerInstanceWrapper for (AppProvider provider : _deploymentManager.getAppProviders()) { - if (provider instanceof OSGiAppProvider) + //if (provider instanceof OSGiAppProvider) + if (provider instanceof BundleWebAppProvider) { - _provider = (OSGiAppProvider) provider; + //_provider = (OSGiAppProvider) provider; + _provider = (BundleWebAppProvider)provider; break; } } @@ -335,10 +338,12 @@ public class ServerInstanceWrapper // 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())); + //_provider = new OSGiAppProvider(); + //_provider.setMonitoredDirResource(Resource.newResource(getDefaultOSGiContextsHome(new File(System.getProperty("jetty.home"))).toURI())); + _provider = new BundleWebAppProvider(this); + System.err.println("ADDED NEW BUNDLE WEBAPP PROVIDER"); } - catch (IOException e) + catch (Exception e) { LOG.warn(e); } @@ -374,10 +379,6 @@ public class ServerInstanceWrapper return new File(jettyHome, "/contexts"); } - File getOSGiContextsHome() - { - return _provider.getContextXmlDirAsFile(); - } /** * @return the urls in this string. @@ -391,7 +392,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) { @@ -415,7 +416,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 00000000000..c5d4a8e9eec --- /dev/null +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/BundleFileLocatorHelperFactory.java @@ -0,0 +1,41 @@ +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/JettyContextHandlerServiceTracker.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java index 5edbfce6c5d..e1d5d8e65ec 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 @@ -17,6 +17,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; +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; @@ -34,6 +35,7 @@ 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 @@ -54,7 +56,11 @@ import org.osgi.framework.ServiceReference; */ public class JettyContextHandlerServiceTracker implements ServiceListener { - private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName()); + private static Logger __logger = Log.getLogger(JettyContextHandlerServiceTracker.class.getName()); + + public static final String FILTER = "(objectclass=" + BundleWebAppProvider.class.getName() + ")"; + + /** New style: ability to manage multiple jetty instances */ private final IManagedJettyServerRegistry _registry; @@ -71,12 +77,21 @@ public class JettyContextHandlerServiceTracker implements ServiceListener /** in charge of detecting changes in the osgi contexts home folder. */ private Scanner _scanner; + + //track all instances of deployers of webapps as bundles + ServiceTracker _serviceTracker; + /** * @param registry */ public JettyContextHandlerServiceTracker(IManagedJettyServerRegistry registry) 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 @@ -127,7 +142,29 @@ public class JettyContextHandlerServiceTracker implements ServiceListener reloadJettyContextHandler(filename, osgiContextHomeFolderCanonicalPath); } }); - + } + + public BundleWebAppProvider getDeployer(String managedServerName) + { + if (managedServerName == null) + managedServerName = OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME; + + ServiceReference sr = null; + ServiceReference[] references = _serviceTracker.getServiceReferences(); + if (references != null) + { + for (ServiceReference ref:references) + { + String name = (String)ref.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME); + if (managedServerName.equalsIgnoreCase(name)) + sr = ref; + } + } + + if (sr != null) + return (BundleWebAppProvider)_serviceTracker.getService(sr); + + return null; } /** @@ -135,6 +172,9 @@ public class JettyContextHandlerServiceTracker implements ServiceListener * * @param ev The ServiceEvent object. */ + /** + * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent) + */ public void serviceChanged(ServiceEvent ev) { ServiceReference sr = ev.getServiceReference(); @@ -143,7 +183,9 @@ public class JettyContextHandlerServiceTracker implements ServiceListener case ServiceEvent.MODIFIED: case ServiceEvent.UNREGISTERING: { - ContextHandler ctxtHandler = unregisterInIndex(ev.getServiceReference()); + //TODO unregister a ContextHandler, either with the BundleWebAppDeployer or with a BundleContextAppDeployer + + /* ContextHandler ctxtHandler = unregisterInIndex(ev.getServiceReference()); if (ctxtHandler != null && !ctxtHandler.isStopped()) { try @@ -154,7 +196,7 @@ public class JettyContextHandlerServiceTracker implements ServiceListener { __logger.warn(e); } - } + }*/ } if (ev.getType() == ServiceEvent.UNREGISTERING) { @@ -175,7 +217,23 @@ public class JettyContextHandlerServiceTracker implements ServiceListener // is configured elsewhere. return; } - String contextFilePath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH); + + //Get a jetty deployer targetted to the named server instance, or the default one if not named + if (contextHandler instanceof WebAppContext) + { + WebAppContext webApp = (WebAppContext)contextHandler; + + String whichServer = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME); + BundleWebAppProvider deployer = getDeployer(whichServer); + deployer.serviceAdded(sr, webApp); + } + else + { + //TODO get a reference to a jetty deployer that is happy to deploy plain ContextHandlers + } + + + /* 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. @@ -274,8 +332,8 @@ public class JettyContextHandlerServiceTracker implements ServiceListener { __logger.warn(e); } + */ } - } break; } } @@ -375,10 +433,11 @@ public class JettyContextHandlerServiceTracker implements ServiceListener private IWebBundleDeployerHelper getWebBundleDeployerHelp(ServiceReference sr) { - if (_registry == null) { return null; } + return null; + /* 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; + 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 a4052085ed1..5c4cc383996 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 @@ -30,6 +30,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; @@ -82,12 +83,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/WebBundleDeployerHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java deleted file mode 100644 index 2d8cc330eda..00000000000 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java +++ /dev/null @@ -1,904 +0,0 @@ -// ======================================================================== -// Copyright (c) 2009 Intalio, Inc. -// ------------------------------------------------------------------------ -// 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 -// 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. -// Contributors: -// Hugues Malphettes - initial API and implementation -// ======================================================================== -package org.eclipse.jetty.osgi.boot.internal.webapp; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.TreeMap; - -import org.eclipse.jetty.deploy.ContextDeployer; -import org.eclipse.jetty.osgi.boot.OSGiWebappConstants; -import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper; -import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelper; -import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper; -import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer; -import org.eclipse.jetty.osgi.boot.utils.internal.DefaultBundleClassLoaderHelper; -import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper; -import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker; -import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.util.IO; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.resource.Resource; -import org.eclipse.jetty.util.resource.ResourceCollection; -import org.eclipse.jetty.webapp.FragmentConfiguration; -import org.eclipse.jetty.webapp.TagLibConfiguration; -import org.eclipse.jetty.webapp.WebAppContext; -import org.eclipse.jetty.webapp.WebInfConfiguration; -import org.eclipse.jetty.xml.XmlConfiguration; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.BundleReference; -import org.osgi.service.packageadmin.PackageAdmin; -import org.osgi.util.tracker.ServiceTracker; -import org.xml.sax.SAXException; - -/** - * Bridges the jetty deployers with the OSGi lifecycle where applications are - * managed inside OSGi-bundles. - *

- * This class should be called as a consequence of the activation of a new - * service that is a ContextHandler.
- * This way the new webapps are exposed as OSGi services. - *

- *

- * Helper methods to register a bundle that is a web-application or a context. - *

- * Limitations: - *
    - *
  • support for jarred webapps is somewhat limited.
  • - *
- */ -public class WebBundleDeployerHelper implements IWebBundleDeployerHelper -{ - - private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName()); - - private static boolean INITIALIZED = false; - - /** - * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports - * equinox and apache-felix fragment bundles that are specific to an OSGi - * implementation should set a different implementation. - */ - public static BundleClassLoaderHelper BUNDLE_CLASS_LOADER_HELPER = null; - - /** - * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports - * equinox and apache-felix fragment bundles that are specific to an OSGi - * implementation should set a different implementation. - */ - public static BundleFileLocatorHelper BUNDLE_FILE_LOCATOR_HELPER = null; - - /** - * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports - * equinox and apache-felix fragment bundles that are specific to an OSGi - * implementation should set a different implementation. - *

- * Several of those objects can be added here: For example we could have an - * optional fragment that setups a specific implementation of JSF for the - * whole of jetty-osgi. - *

- */ - public static Collection JSP_REGISTRATION_HELPERS = new ArrayList(); - - /** - * this class loader loads the jars inside {$jetty.home}/lib/ext it is meant - * as a migration path and for jars that are not OSGi ready. also gives - * access to the jsp jars. - */ - // private URLClassLoader _libExtClassLoader; - - private ServerInstanceWrapper _wrapper; - - public WebBundleDeployerHelper(ServerInstanceWrapper wrapper) - { - staticInit(); - _wrapper = wrapper; - } - - // Inject the customizing classes that might be defined in fragment bundles. - public static synchronized void staticInit() - { - if (!INITIALIZED) - { - INITIALIZED = true; - // setup the custom BundleClassLoaderHelper - try - { - BUNDLE_CLASS_LOADER_HELPER = (BundleClassLoaderHelper) Class.forName(BundleClassLoaderHelper.CLASS_NAME).newInstance(); - } - catch (Throwable t) - { - // System.err.println("support for equinox and felix"); - BUNDLE_CLASS_LOADER_HELPER = new DefaultBundleClassLoaderHelper(); - } - // setup the custom FileLocatorHelper - try - { - BUNDLE_FILE_LOCATOR_HELPER = (BundleFileLocatorHelper) Class.forName(BundleFileLocatorHelper.CLASS_NAME).newInstance(); - } - catch (Throwable t) - { - // System.err.println("no jsp/jasper support"); - BUNDLE_FILE_LOCATOR_HELPER = new DefaultFileLocatorHelper(); - } - } - } - - /** - * Deploy a new web application on the jetty server. - * - * @param bundle The bundle - * @param webappFolderPath The path to the root of the webapp. Must be a - * path relative to bundle; either an absolute path. - * @param contextPath The context path. Must start with "/" - * @param extraClasspath - * @param overrideBundleInstallLocation - * @param requireTldBundle The list of bundles's symbolic names that contain - * tld files that are required by this WAB. - * @param webXmlPath - * @param defaultWebXmlPath TODO: parameter description - * @return The contexthandler created and started - * @throws Exception - */ - public WebAppContext registerWebapplication(Bundle bundle, String webappFolderPath, String contextPath, String extraClasspath, - String overrideBundleInstallLocation, String requireTldBundle, String webXmlPath, String defaultWebXmlPath, - WebAppContext webAppContext) - throws Exception - { - File bundleInstall = overrideBundleInstallLocation == null ? BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(bundle) : new File(overrideBundleInstallLocation); - File webapp = null; - URL baseWebappInstallURL = null; - - if (webappFolderPath != null && webappFolderPath.length() != 0 && !webappFolderPath.equals(".")) - { - if (webappFolderPath.startsWith("/") || webappFolderPath.startsWith("file:")) - { - webapp = new File(webappFolderPath); - } - else if (bundleInstall != null && bundleInstall.isDirectory()) - { - webapp = new File(bundleInstall, webappFolderPath); - } - else if (bundleInstall != null) - { - Enumeration urls = BUNDLE_FILE_LOCATOR_HELPER.findEntries(bundle, webappFolderPath); - if (urls != null && urls.hasMoreElements()) - { - baseWebappInstallURL = urls.nextElement(); - } - } - } - else - { - webapp = bundleInstall; - } - if (baseWebappInstallURL == null && (webapp == null || !webapp.exists())) - { - throw new IllegalArgumentException("Unable to locate " + webappFolderPath - + " inside " - + (bundleInstall != null ? bundleInstall.getAbsolutePath() : "unlocated bundle '" + bundle.getSymbolicName()+ "'")); - } - if (baseWebappInstallURL == null && webapp != null) - { - baseWebappInstallURL = webapp.toURI().toURL(); - } - return registerWebapplication(bundle, webappFolderPath, baseWebappInstallURL, contextPath, extraClasspath, bundleInstall, requireTldBundle, webXmlPath, - defaultWebXmlPath, webAppContext); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper# - * registerWebapplication(org.osgi.framework.Bundle, java.lang.String, - * java.io.File, java.lang.String, java.lang.String, java.io.File, - * java.lang.String, java.lang.String) - */ - private WebAppContext registerWebapplication(Bundle contributor, String pathInBundleToWebApp, URL baseWebappInstallURL, String contextPath, - String extraClasspath, File bundleInstall, String requireTldBundle, String webXmlPath, - String defaultWebXmlPath, WebAppContext context) - throws Exception - { - ClassLoader contextCl = Thread.currentThread().getContextClassLoader(); - String[] oldServerClasses = null; - - try - { - - // apply any META-INF/context.xml file that is found to configure - // the webapp first - applyMetaInfContextXml(contributor, context); - - // make sure we provide access to all the jetty bundles by going - // through this bundle. - OSGiWebappClassLoader composite = createWebappClassLoader(contributor); - // configure with access to all jetty classes and also all the - // classes - // that the contributor gives access to. - Thread.currentThread().setContextClassLoader(composite); - - // converts bundleentry: protocol - baseWebappInstallURL = DefaultFileLocatorHelper.getLocalURL(baseWebappInstallURL); - - context.setWar(baseWebappInstallURL.toString()); - context.setContextPath(contextPath); - context.setExtraClasspath(extraClasspath); - - if (webXmlPath != null && webXmlPath.length() != 0) - { - File webXml = null; - if (webXmlPath.startsWith("/") || webXmlPath.startsWith("file:/")) - { - webXml = new File(webXmlPath); - } - else - { - webXml = new File(bundleInstall, webXmlPath); - } - if (webXml.exists()) - { - context.setDescriptor(webXml.getAbsolutePath()); - } - } - - if (defaultWebXmlPath == null || defaultWebXmlPath.length() == 0) - { - // use the one defined by the OSGiAppProvider. - defaultWebXmlPath = _wrapper.getOSGiAppProvider().getDefaultsDescriptor(); - } - if (defaultWebXmlPath != null && defaultWebXmlPath.length() != 0) - { - File defaultWebXml = null; - if (defaultWebXmlPath.startsWith("/") || defaultWebXmlPath.startsWith("file:/")) - { - defaultWebXml = new File(defaultWebXmlPath); - } - else - { - defaultWebXml = new File(bundleInstall, defaultWebXmlPath); - } - if (defaultWebXml.exists()) - { - context.setDefaultsDescriptor(defaultWebXml.getAbsolutePath()); - } - } - - // other parameters that might be defines on the OSGiAppProvider: - context.setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority()); - - configureWebappClassLoader(contributor, context, composite, requireTldBundle); - configureWebAppContext(context, contributor, requireTldBundle); - - // @see - // org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext) - // during initialization of the webapp all the jetty packages are - // visible - // through the webapp classloader. - oldServerClasses = context.getServerClasses(); - context.setServerClasses(null); - - _wrapper.getOSGiAppProvider().addContext(contributor, pathInBundleToWebApp, context); - - // support for patch resources. ideally this should be done inside a - // configurator. - List patchResources = (List) context.getAttribute(WebInfConfiguration.RESOURCE_URLS + ".patch"); - if (patchResources != null) - { - LinkedList resourcesPath = new LinkedList(); - // place the patch resources at the beginning of the lookup - // path. - resourcesPath.addAll(patchResources); - // then place the ones from the host web bundle. - Resource hostResources = context.getBaseResource(); - if (hostResources instanceof ResourceCollection) - { - for (Resource re : ((ResourceCollection) hostResources).getResources()) - { - resourcesPath.add(re); - } - } - else - { - resourcesPath.add(hostResources); - } - - ResourceCollection rc = new ResourceCollection(resourcesPath.toArray(new Resource[resourcesPath.size()])); - context.setBaseResource(rc); - } - - return context; - } - finally - { - if (context != null && oldServerClasses != null) - { - context.setServerClasses(oldServerClasses); - } - Thread.currentThread().setContextClassLoader(contextCl); - } - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper# - * unregister(org.eclipse.jetty.server.handler.ContextHandler) - */ - public void unregister(ContextHandler contextHandler) - throws Exception - { - _wrapper.getOSGiAppProvider().removeContext(contextHandler); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper# - * registerContext(org.osgi.framework.Bundle, java.lang.String, - * java.lang.String, java.lang.String) - */ - public ContextHandler registerContext(Bundle contributor, String contextFileRelativePath, String extraClasspath, String overrideBundleInstallLocation, - String requireTldBundle, ContextHandler handler) - throws Exception - { - File contextsHome = _wrapper.getOSGiAppProvider().getContextXmlDirAsFile(); - if (contextsHome != null) - { - File prodContextFile = new File(contextsHome, contributor.getSymbolicName() + "/" + contextFileRelativePath); - if (prodContextFile.exists()) { return registerContext(contributor, contextFileRelativePath, prodContextFile, extraClasspath, - overrideBundleInstallLocation, requireTldBundle, handler); } - } - File rootFolder = overrideBundleInstallLocation != null ? Resource.newResource(overrideBundleInstallLocation).getFile() : BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(contributor); - File contextFile = rootFolder != null ? new File(rootFolder, contextFileRelativePath) : null; - if (contextFile != null && contextFile.exists()) - { - return registerContext(contributor, contextFileRelativePath, contextFile, extraClasspath, overrideBundleInstallLocation, requireTldBundle, handler); - } - else - { - if (contextFileRelativePath.startsWith("./")) - { - contextFileRelativePath = contextFileRelativePath.substring(1); - } - if (!contextFileRelativePath.startsWith("/")) - { - contextFileRelativePath = "/" + contextFileRelativePath; - } - - URL contextURL = contributor.getEntry(contextFileRelativePath); - if (contextURL != null) - { - Resource r = Resource.newResource(contextURL); - return registerContext(contributor, contextFileRelativePath, r.getInputStream(), extraClasspath, overrideBundleInstallLocation, - requireTldBundle, handler); - } - throw new IllegalArgumentException("Could not find the context " + "file " - + contextFileRelativePath - + " for the bundle " - + contributor.getSymbolicName() - + (overrideBundleInstallLocation != null ? " using the install location " + overrideBundleInstallLocation : "")); - } - } - - /** - * This type of registration relies on jetty's complete context xml file. - * Context encompasses jndi and all other things. This makes the definition - * of the webapp a lot more self-contained. - * - * @param webapp - * @param contextPath - * @param classInBundle - * @throws Exception - */ - private ContextHandler registerContext(Bundle contributor, String pathInBundle, File contextFile, String extraClasspath, - String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler) - throws Exception - { - InputStream contextFileInputStream = null; - try - { - contextFileInputStream = new BufferedInputStream(new FileInputStream(contextFile)); - return registerContext(contributor, pathInBundle, contextFileInputStream, extraClasspath, overrideBundleInstallLocation, requireTldBundle, handler); - } - finally - { - IO.close(contextFileInputStream); - } - } - - /** - * @param contributor - * @param contextFileInputStream - * @return The ContextHandler created and registered or null if it did not - * happen. - * @throws Exception - */ - private ContextHandler registerContext(Bundle contributor, String pathInsideBundle, InputStream contextFileInputStream, String extraClasspath, - String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler) - throws Exception - { - ClassLoader contextCl = Thread.currentThread().getContextClassLoader(); - String[] oldServerClasses = null; - WebAppContext webAppContext = null; - try - { - // make sure we provide access to all the jetty bundles by going - // through this bundle. - OSGiWebappClassLoader composite = createWebappClassLoader(contributor); - // configure with access to all jetty classes and also all the - // classes - // that the contributor gives access to. - Thread.currentThread().setContextClassLoader(composite); - ContextHandler context = createContextHandler(handler, contributor, contextFileInputStream, extraClasspath, overrideBundleInstallLocation, - requireTldBundle); - if (context == null) { return null;// did not happen - } - - // ok now register this webapp. we checked when we started jetty - // that there - // was at least one such handler for webapps. - // the actual registration must happen via the new Deployment API. - // _ctxtHandler.addHandler(context); - - configureWebappClassLoader(contributor, context, composite, requireTldBundle); - if (context instanceof WebAppContext) - { - webAppContext = (WebAppContext) context; - // @see - // org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext) - oldServerClasses = webAppContext.getServerClasses(); - webAppContext.setServerClasses(null); - } - _wrapper.getOSGiAppProvider().addContext(contributor, pathInsideBundle, context); - return context; - } - finally - { - if (webAppContext != null) - { - webAppContext.setServerClasses(oldServerClasses); - } - Thread.currentThread().setContextClassLoader(contextCl); - } - - } - - /** - * Applies the properties of WebAppDeployer as defined in jetty.xml. - * - * @see {WebAppDeployer#scan} around the comment - * // configure it - */ - protected void configureWebAppContext(ContextHandler wah, Bundle contributor, String requireTldBundle) - throws IOException - { - // rfc66 - wah.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, contributor.getBundleContext()); - - // spring-dm-1.2.1 looks for the BundleContext as a different attribute. - // not a spec... but if we want to support - // org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext - // then we need to do this to: - wah.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), contributor.getBundleContext()); - - // also pass the bundle directly. sometimes a bundle does not have a - // bundlecontext. - // it is still useful to have access to the Bundle from the servlet - // context. - wah.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, contributor); - - // pass the value of the require tld bundle so that the - // TagLibOSGiConfiguration - // can pick it up. - wah.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundle); - - Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(contributor); - if (fragments != null && fragments.length != 0) - { - // sorted extra resource base found in the fragments. - // the resources are either overriding the resourcebase found in the - // web-bundle - // or appended. - // amongst each resource we sort them according to the alphabetical - // order - // of the name of the internal folder and the symbolic name of the - // fragment. - // this is useful to make sure that the lookup path of those - // resource base defined by fragments is always the same. - // This natural order could be abused to define the order in which - // the base resources are - // looked up. - TreeMap patchResourcesPath = new TreeMap(); - TreeMap appendedResourcesPath = new TreeMap(); - for (Bundle frag : fragments) - { - String fragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH); - String patchFragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH); - if (fragFolder != null) - { - URL fragUrl = frag.getEntry(fragFolder); - if (fragUrl == null) { throw new IllegalArgumentException("Unable to locate " + fragFolder - + " inside " - + " the fragment '" - + frag.getSymbolicName() - + "'"); } - fragUrl = DefaultFileLocatorHelper.getLocalURL(fragUrl); - String key = fragFolder.startsWith("/") ? fragFolder.substring(1) : fragFolder; - appendedResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(fragUrl)); - } - if (patchFragFolder != null) - { - URL patchFragUrl = frag.getEntry(patchFragFolder); - if (patchFragUrl == null) { throw new IllegalArgumentException("Unable to locate " + patchFragUrl - + " inside " - + " the fragment '" - + frag.getSymbolicName() - + "'"); } - patchFragUrl = DefaultFileLocatorHelper.getLocalURL(patchFragUrl); - String key = patchFragFolder.startsWith("/") ? patchFragFolder.substring(1) : patchFragFolder; - patchResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(patchFragUrl)); - } - } - if (!appendedResourcesPath.isEmpty()) - { - wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, new ArrayList(appendedResourcesPath.values())); - } - if (!patchResourcesPath.isEmpty()) - { - wah.setAttribute(WebInfConfiguration.RESOURCE_URLS + ".patch", new ArrayList(patchResourcesPath.values())); - } - - if (wah instanceof WebAppContext) - { - // This is the equivalent of what MetaInfConfiguration does. For - // OSGi bundles without the JarScanner - WebAppContext webappCtxt = (WebAppContext) wah; - // take care of the web-fragments, meta-inf resources and tld - // resources: - // similar to what MetaInfConfiguration does. - List frags = (List) wah.getAttribute(FragmentConfiguration.FRAGMENT_RESOURCES); - List resfrags = (List) wah.getAttribute(WebInfConfiguration.RESOURCE_URLS); - List tldfrags = (List) wah.getAttribute(TagLibConfiguration.TLD_RESOURCES); - for (Bundle frag : fragments) - { - URL webFrag = frag.getEntry("/META-INF/web-fragment.xml"); - Enumeration resEnum = frag.findEntries("/META-INF/resources", "*", true); - Enumeration tldEnum = frag.findEntries("/META-INF", "*.tld", false); - if (webFrag != null || (resEnum != null && resEnum.hasMoreElements()) || (tldEnum != null && tldEnum.hasMoreElements())) - { - try - { - File fragFile = BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(frag); - // add it to the webinf jars collection: - // no need to check that it was not there yet: it - // was not there yet for sure. - Resource fragFileAsResource = Resource.newResource(fragFile.toURI()); - webappCtxt.getMetaData().addWebInfJar(fragFileAsResource); - - if (webFrag != null) - { - if (frags == null) - { - frags = new ArrayList(); - wah.setAttribute(FragmentConfiguration.FRAGMENT_RESOURCES, frags); - } - frags.add(fragFileAsResource); - } - if (resEnum != null && resEnum.hasMoreElements()) - { - URL resourcesEntry = frag.getEntry("/META-INF/resources/"); - if (resourcesEntry == null) - { - // probably we found some fragments to a - // bundle. - // those are already contributed. - // so we skip this. - } - else - { - if (resfrags == null) - { - resfrags = new ArrayList(); - wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, resfrags); - } - resfrags.add(Resource.newResource(DefaultFileLocatorHelper.getLocalURL(resourcesEntry))); - } - } - if (tldEnum != null && tldEnum.hasMoreElements()) - { - if (tldfrags == null) - { - tldfrags = new ArrayList(); - wah.setAttribute(TagLibConfiguration.TLD_RESOURCES, tldfrags); - } - while (tldEnum.hasMoreElements()) - { - URL tldUrl = tldEnum.nextElement(); - tldfrags.add(Resource.newResource(DefaultFileLocatorHelper.getLocalURL(tldUrl))); - } - } - } - catch (Exception e) - { - __logger.warn("Unable to locate the bundle " + frag.getBundleId(), e); - } - } - } - } - } - } - - /** - * @See {@link ContextDeployer#scan} - * @param contextFile - * @return - */ - protected ContextHandler createContextHandler(ContextHandler handlerToConfigure, Bundle bundle, File contextFile, String extraClasspath, - String overrideBundleInstallLocation, String requireTldBundle) - { - try - { - return createContextHandler(handlerToConfigure, bundle, new BufferedInputStream(new FileInputStream(contextFile)), extraClasspath, - overrideBundleInstallLocation, requireTldBundle); - } - catch (FileNotFoundException e) - { - __logger.warn(e); - } - return null; - } - - /** - * @See {@link ContextDeployer#scan} - * @param contextFile - * @return - */ - @SuppressWarnings("unchecked") - protected ContextHandler createContextHandler(ContextHandler handlerToConfigure, Bundle bundle, InputStream contextInputStream, String extraClasspath, - String overrideBundleInstallLocation, String requireTldBundle) - { - /* - * Do something identical to what the ContextProvider would have done: - * XmlConfiguration xmlConfiguration=new - * XmlConfiguration(resource.getURL()); HashMap properties = new - * HashMap(); properties.put("Server", _contexts.getServer()); if - * (_configMgr!=null) properties.putAll(_configMgr.getProperties()); - * - * xmlConfiguration.setProperties(properties); ContextHandler - * context=(ContextHandler)xmlConfiguration.configure(); - * context.setAttributes(new AttributesMap(_contextAttributes)); - */ - try - { - XmlConfiguration xmlConfiguration = new XmlConfiguration(contextInputStream); - HashMap properties = new HashMap(); - properties.put("Server", _wrapper.getServer()); - - // insert the bundle's location as a property. - setThisBundleHomeProperty(bundle, properties, overrideBundleInstallLocation); - xmlConfiguration.getProperties().putAll(properties); - - ContextHandler context = null; - if (handlerToConfigure == null) - { - context = (ContextHandler) xmlConfiguration.configure(); - } - else - { - xmlConfiguration.configure(handlerToConfigure); - context = handlerToConfigure; - } - - if (context instanceof WebAppContext) - { - ((WebAppContext) context).setExtraClasspath(extraClasspath); - ((WebAppContext) context).setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority()); - if (_wrapper.getOSGiAppProvider().getDefaultsDescriptor() != null && _wrapper.getOSGiAppProvider().getDefaultsDescriptor().length() != 0) - { - ((WebAppContext) context).setDefaultsDescriptor(_wrapper.getOSGiAppProvider().getDefaultsDescriptor()); - } - } - - configureWebAppContext(context, bundle, requireTldBundle); - return context; - } - catch (FileNotFoundException e) - { - return null; - } - catch (SAXException e) - { - __logger.warn(e); - } - catch (IOException e) - { - __logger.warn(e); - } - catch (Throwable e) - { - __logger.warn(e); - } - finally - { - IO.close(contextInputStream); - } - return null; - } - - /** - * Configure a classloader onto the context. If the context is a - * WebAppContext, build a WebAppClassLoader that has access to all the jetty - * classes thanks to the classloader of the JettyBootStrapper bundle and - * also has access to the classloader of the bundle that defines this - * context. - *

- * If the context is not a WebAppContext, same but with a simpler - * URLClassLoader. Note that the URLClassLoader is pretty much fake: it - * delegate all actual classloading to the parent classloaders. - *

- *

- * The URL[] returned by the URLClassLoader create contained specifically - * the jars that some j2ee tools expect and look into. For example the jars - * that contain tld files for jasper's jstl support. - *

- *

- * Also as the jars in the lib folder and the classes in the classes folder - * might already be in the OSGi classloader we filter them out of the - * WebAppClassLoader - *

- * - * @param context - * @param contributor - * @param webapp - * @param contextPath - * @param classInBundle - * @throws Exception - */ - protected void configureWebappClassLoader(Bundle contributor, ContextHandler context, OSGiWebappClassLoader webappClassLoader, String requireTldBundle) - throws Exception - { - if (context instanceof WebAppContext) - { - WebAppContext webappCtxt = (WebAppContext) context; - context.setClassLoader(webappClassLoader); - webappClassLoader.setWebappContext(webappCtxt); - - String pathsToRequiredBundles = getPathsToRequiredBundles(context, contributor, requireTldBundle); - if (pathsToRequiredBundles != null) webappClassLoader.addClassPath(pathsToRequiredBundles); - } - else - { - context.setClassLoader(webappClassLoader); - } - } - - /** - * No matter what the type of webapp, we create a WebappClassLoader. - */ - protected OSGiWebappClassLoader createWebappClassLoader(Bundle contributor) - throws Exception - { - // we use a temporary WebAppContext object. - // if this is a real webapp we will set it on it a bit later: once we - // know. - OSGiWebappClassLoader webappClassLoader = new OSGiWebappClassLoader(_wrapper.getParentClassLoaderForWebapps(), new WebAppContext(), contributor, - BUNDLE_CLASS_LOADER_HELPER); - return webappClassLoader; - } - - protected void applyMetaInfContextXml(Bundle bundle, ContextHandler contextHandler) - throws Exception - { - if (bundle == null) return; - if (contextHandler == null) return; - - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - __logger.info("Context classloader = " + cl); - try - { - Thread.currentThread().setContextClassLoader(_wrapper.getParentClassLoaderForWebapps()); - - // find if there is a META-INF/context.xml file - URL contextXmlUrl = bundle.getEntry("/META-INF/jetty-webapp-context.xml"); - if (contextXmlUrl == null) return; - - // Apply it just as the standard jetty ContextProvider would do - __logger.info("Applying " + contextXmlUrl + " to " + contextHandler); - - XmlConfiguration xmlConfiguration = new XmlConfiguration(contextXmlUrl); - HashMap properties = new HashMap(); - properties.put("Server", _wrapper.getServer()); - xmlConfiguration.getProperties().putAll(properties); - xmlConfiguration.configure(contextHandler); - } - finally - { - Thread.currentThread().setContextClassLoader(cl); - } - } - - /** - * Set the property "this.bundle.install" to point to the location - * of the bundle. Useful when is - * used. - */ - private void setThisBundleHomeProperty(Bundle bundle, HashMap properties, String overrideBundleInstallLocation) - { - try - { - File location = overrideBundleInstallLocation != null ? new File(overrideBundleInstallLocation) : BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(bundle); - properties.put("this.bundle.install", location.getCanonicalPath()); - properties.put("this.bundle.install.url", bundle.getEntry("/").toString()); - } - catch (Throwable t) - { - __logger.warn("Unable to set 'this.bundle.install' " + " for the bundle " + bundle.getSymbolicName(), t); - } - } - - private String getPathsToRequiredBundles(ContextHandler context, Bundle bundle, String requireTldBundle) - throws Exception - { - if (requireTldBundle == null) return null; - - StringBuilder paths = new StringBuilder(); - PackageAdmin packAdmin = getBundleAdmin(); - DefaultFileLocatorHelper fileLocatorHelper = new DefaultFileLocatorHelper(); - - String[] symbNames = requireTldBundle.split(", "); - - for (String symbName : symbNames) - { - Bundle[] bs = packAdmin.getBundles(symbName, null); - if (bs == null || bs.length == 0) { throw new IllegalArgumentException("Unable to locate the bundle '" + symbName - + "' specified in the " - + OSGiWebappConstants.REQUIRE_TLD_BUNDLE - + " of the manifest of " - + (bundle == null ? "unknown" : bundle.getSymbolicName())); } - - File f = fileLocatorHelper.getBundleInstallLocation(bs[0]); - if (paths.length() > 0) paths.append(", "); - __logger.debug("getPathsToRequiredBundles: bundle path=" + bs[0].getLocation() + " uri=" + f.toURI()); - paths.append(f.toURI().toURL().toString()); - } - - return paths.toString(); - } - - private PackageAdmin getBundleAdmin() - { - Bundle bootBundle = ((BundleReference) OSGiWebappConstants.class.getClassLoader()).getBundle(); - ServiceTracker serviceTracker = new ServiceTracker(bootBundle.getBundleContext(), PackageAdmin.class.getName(), null); - serviceTracker.open(); - - return (PackageAdmin) serviceTracker.getService(); - } -} 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 134b890135a..3a6a42f6f39 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 @@ -13,16 +13,25 @@ package org.eclipse.jetty.osgi.boot.internal.webapp; import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; import java.util.Dictionary; +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.internal.serverfactory.IManagedJettyServerRegistry; +import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper; +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.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 @@ -41,16 +50,28 @@ import org.osgi.util.tracker.BundleTrackerCustomizer; *
  • Jetty-ContextFilePath
  • * *

    - * 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); + + public static Collection JSP_REGISTRATION_HELPERS = new ArrayList(); + public static final String FILTER = "(&(objectclass=" + BundleWebAppProvider.class.getName() + ")"+ + "("+OSGiServerConstants.MANAGED_JETTY_SERVER_NAME+"="+OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME+"))"; + + private ServiceTracker _serviceTracker; + + public WebBundleTrackerCustomizer () + throws Exception + { + Bundle myBundle = FrameworkUtil.getBundle(this.getClass()); + + //track all instances of deployers of webapps as bundles + _serviceTracker = new ServiceTracker(myBundle.getBundleContext(), FrameworkUtil.createFilter(FILTER),null); + _serviceTracker.open(); + } /** * A bundle is being added to the BundleTracker. @@ -76,8 +97,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) { @@ -142,123 +162,25 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer */ 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. - } - } - } + if (bundle == null) + return false; + + //It might be a webapp bundle + //If it is, it will be deployed to our default jetty Server instance. + //Get a reference to a jetty BundleWebAppProvider as an osgi service that can do the deployment - 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; - } + BundleWebAppProvider deployer = (BundleWebAppProvider)_serviceTracker.getService(); + if (deployer != null) + return deployer.bundleAdded(bundle); + return false; + } + + 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. + BundleWebAppProvider deployer = (BundleWebAppProvider)_serviceTracker.getService(); + if (deployer != null) + deployer.bundleRemoved(bundle); } - } 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 00000000000..cc1a2871e38 --- /dev/null +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/BundleClassLoaderHelperFactory.java @@ -0,0 +1,44 @@ +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 44b7d8769af..f37178a6146 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 @@ -83,5 +83,31 @@ public interface BundleFileLocatorHelper * @return null or all the entries found for that path. */ public Enumeration 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 + *

    + * Get a URL to the bundle entry that uses a common protocol (i.e. file: + * jar: or http: etc.). + *

    + * + * @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 + *

    + * 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 + *

    + */ + public URL getFileURL(URL url); } 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 1a37a046c62..3eae16f5104 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 @@ -14,6 +14,7 @@ package org.eclipse.jetty.osgi.boot.utils; import java.net.URL; +import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.osgi.boot.OSGiAppProvider; /** @@ -49,6 +50,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/DefaultFileLocatorHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java index 94008f633f5..5a733768fa8 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 @@ -292,7 +292,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())) { @@ -328,7 +328,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper * protocol *

    */ - public static URL getFileURL(URL url) + public URL getFileURL(URL url) { if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol())) { diff --git a/jetty-osgi/pom.xml b/jetty-osgi/pom.xml index 9f0ce598089..877c6c37382 100644 --- a/jetty-osgi/pom.xml +++ b/jetty-osgi/pom.xml @@ -25,6 +25,7 @@ jetty-osgi-boot-warurl jetty-osgi-httpservice jetty-osgi-equinoxtools + test-jetty-osgi-webapp test-jetty-osgi diff --git a/jetty-osgi/test-jetty-osgi-webapp/pom.xml b/jetty-osgi/test-jetty-osgi-webapp/pom.xml new file mode 100644 index 00000000000..0b6cfe088cc --- /dev/null +++ b/jetty-osgi/test-jetty-osgi-webapp/pom.xml @@ -0,0 +1,129 @@ + + + org.eclipse.jetty.osgi + jetty-osgi-project + 7.6.4-SNAPSHOT + ../pom.xml + + 4.0.0 + test-jetty-osgi-webapp + Jetty :: OSGi :: WebApp + Test Jetty OSGi Webapp bundle + + ${project.groupId}.webapp + + + + org.eclipse.jetty + jetty-webapp + + + org.eclipse.osgi + org.eclipse.osgi + + + org.eclipse.osgi + org.eclipse.osgi.services + + + + + + + src/main/resources + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + artifact-jar + + jar + + + + test-jar + + test-jar + + + + + + target/classes/META-INF/MANIFEST.MF + + + + + org.apache.felix + maven-bundle-plugin + true + + + bundle-manifest + process-classes + + manifest + + + + + + org.eclipse.jetty.osgi.testapp;singleton:=true + Jetty OSGi Test WebApp + com.acme.osgi.Activator + J2SE-1.5 + + <_nouses>true + + org.osgi.framework, + org.osgi.service.cm;version="1.2.0", + org.osgi.service.packageadmin, + org.osgi.service.startlevel;version="1.0.o", + org.osgi.service.url;version="1.0.0", + org.osgi.util.tracker;version="1.3.0", + org.slf4j;resolution:=optional, + org.slf4j.spi;resolution:=optional, + org.slf4j.helpers;resolution:=optional, + org.xml.sax, + org.xml.sax.helpers, + * + + org.eclipse.jetty.*;version="[7.6,8)" + + + + + + + + + diff --git a/jetty-osgi/test-jetty-osgi-webapp/src/main/java/com/acme/osgi/Activator.java b/jetty-osgi/test-jetty-osgi-webapp/src/main/java/com/acme/osgi/Activator.java new file mode 100644 index 00000000000..b1a95f07843 --- /dev/null +++ b/jetty-osgi/test-jetty-osgi-webapp/src/main/java/com/acme/osgi/Activator.java @@ -0,0 +1,60 @@ +// ======================================================================== +// Copyright (c) 2012 Intalio, Inc. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +package com.acme.osgi; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.webapp.WebAppContext; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceRegistration; +import org.osgi.util.tracker.BundleTracker; + +/** + * Bootstrap a webapp + * + * + */ +public class Activator implements BundleActivator +{ + + /** + * + * @param context + */ + public void start(BundleContext context) throws Exception + { + WebAppContext webapp = new WebAppContext(); + Dictionary props = new Hashtable(); + props.put("war","."); + props.put("contextPath","/acme"); + //uiProps.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, serverName); + context.registerService(ContextHandler.class.getName(),webapp,props); + } + + /** + * Stop the activator. + * + * @see + * org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception + { + } +} diff --git a/jetty-osgi/test-jetty-osgi-webapp/src/main/resources/index.html b/jetty-osgi/test-jetty-osgi-webapp/src/main/resources/index.html new file mode 100644 index 00000000000..9e62c04bb91 --- /dev/null +++ b/jetty-osgi/test-jetty-osgi-webapp/src/main/resources/index.html @@ -0,0 +1,6 @@ + + +

    Test OSGi WebApp

    +

    Webapp registered by bundle as service successfully deployed.

    + + diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml index 1e792c14633..ba2d2771f39 100644 --- a/jetty-osgi/test-jetty-osgi/pom.xml +++ b/jetty-osgi/test-jetty-osgi/pom.xml @@ -134,6 +134,15 @@ test + + + org.eclipse.jetty.osgi + test-jetty-osgi-webapp + ${project.version} + runtime + + + org.ops4j.pax.exam pax-exam @@ -157,6 +166,12 @@ junit test + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-deployer.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-deployer.xml new file mode 100644 index 00000000000..ac352765627 --- /dev/null +++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-deployer.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern + .*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$ + + + + + + + + + + + + + + + + diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-selector.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-selector.xml new file mode 100644 index 00000000000..46ccc85f525 --- /dev/null +++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-selector.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + 300000 + 2 + false + 8443 + 20000 + 5000 + + + + + diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty.xml new file mode 100644 index 00000000000..30f8ea9d186 --- /dev/null +++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + 10 + 200 + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + 1000 + false + false + + + + + + + java.naming.factory.initial + + + + java.naming.factory.url.pkgs + + + + diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootCore.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootCore.java index 1cd0c65e1f8..f773ef9c6d2 100644 --- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootCore.java +++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootCore.java @@ -121,7 +121,7 @@ public class TestJettyOSGiBootCore * You will get a list of bundles installed by default * plus your testcase, wrapped into a bundle called pax-exam-probe */ - @Test + //@Test public void testHttpService() throws Exception { // ServletContextHandler sch = null; diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootWebAppAsService.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootWebAppAsService.java new file mode 100644 index 00000000000..0398a508ecd --- /dev/null +++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootWebAppAsService.java @@ -0,0 +1,166 @@ +// ======================================================================== +// Copyright (c) 2010 Intalio, Inc. +// ------------------------------------------------------------------------ +// 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. +// Contributors: +// Hugues Malphettes - initial API and implementation +// ======================================================================== +package org.eclipse.jetty.osgi.boot; + +import static org.ops4j.pax.exam.CoreOptions.*; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import junit.framework.Assert; + +import org.eclipse.jetty.client.ContentExchange; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpExchange; +import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.Inject; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.container.def.PaxRunnerOptions; +import org.ops4j.pax.exam.junit.Configuration; +import org.ops4j.pax.exam.junit.JUnit4TestRunner; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +/** + * Pax-Exam to make sure the jetty-osgi-boot can be started along with the httpservice web-bundle. + * Then make sure we can deploy an OSGi service on the top of this. + */ +@RunWith( JUnit4TestRunner.class ) +public class TestJettyOSGiBootWebAppAsService +{ + private static final boolean LOGGING_ENABLED = false; + private static final boolean REMOTE_DEBUGGING = false; + + @Inject + BundleContext bundleContext = null; + + @Configuration + public static Option[] configure() + { + ArrayList