bug #306971 support for taglibs for each WAB. the bundles that contain tlds for a WAB must be listed in the manifest header 'Require-TldBundle

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@2658 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Hugues Malphettes 2011-01-14 03:33:56 +00:00
parent 306947b142
commit 4240756d46
7 changed files with 251 additions and 49 deletions

View File

@ -38,7 +38,9 @@ import org.xml.sax.SAXException;
/**
* Fix various shortcomings with the way jasper parses the tld files.
* Plugs the JSTL tlds assuming that they are packaged with the bundle that contains the JSTL classes.
*
* <p>
* Pluggable tlds at the server level are handled by {@link PluggableWebAppRegistrationCustomizerImpl}.
* </p>
*/
public class WebappRegistrationCustomizerImpl implements WebappRegistrationCustomizer
{
@ -106,8 +108,6 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
}
/**
* TODO: right now only the jetty-jsp bundle is scanned for common taglibs. Should support a way to plug more bundles that contain taglibs.
*
* The jasper TldScanner expects a URLClassloader to parse a jar for the /META-INF/*.tld it may contain. We place the bundles that we know contain such
* tag-libraries. Please note that it will work if and only if the bundle is a jar (!) Currently we just hardcode the bundle that contains the jstl
* implemenation.
@ -133,16 +133,6 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
classesToAddToTheTldBundles.add(jstlClass);
// //should we also take care of the JSF?
// try
// {
// jstlClass = WebappRegistrationCustomizerImpl.class.getClassLoader().loadClass(DEFAUT_JSF_IMPL_CLASS);
// classesToAddToTheTldBundles.add(jstlClass);
// }
// catch (Throwable t)
// {
// //never mind we can live without JSF it is optional.
// }
ArrayList<URL> urls = new ArrayList<URL>();
for (Class<?> cl : classesToAddToTheTldBundles)
{

View File

@ -0,0 +1,154 @@
// ========================================================================
// Copyright (c) 2004-2011 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.osgi.boot.jsp;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.TagLibConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleReference;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.util.tracker.ServiceTracker;
/**
* <p>
* Replacement for {@link TagLibConfiguration} for the OSGi integration.
* </p>
* <p>
* In the case of a WAB, tlds can be located in OSGi bundles that are dependencies
* of the WAB.
* It is expected that each WAB lists the symbolic-names of the bundles that contain
* tld files. The list is defined as the value of the header 'Require-TldBundle'
* </p>
* <p>
* Discussions about this are logged in https://bugs.eclipse.org/bugs/show_bug.cgi?id=306971
* </p>
*/
public class TagLibOSGiConfiguration extends TagLibConfiguration
{
private ServiceTracker packageAdminServiceTracker = null;
/**
* Override the preConfigure; locates the bundles that contain
* tld files according to the value of the manifest header Require-TldBundle.
* <p>
* Set or add to the property TldProcessor.TLDResources the list of located jars
* so that the super class will scan those.
* </p>
*/
public void preConfigure(WebAppContext context) throws Exception
{
String requireTldBundle = (String)context.getAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
if (requireTldBundle != null)
{
Collection<Resource> resources = getRequireTldBundleAsJettyResources(context, requireTldBundle);
if (resources != null && !resources.isEmpty())
{
Collection<Resource> previouslySet = (Collection<Resource>)
context.getAttribute(TagLibConfiguration.TLD_RESOURCES);
if (previouslySet != null)
{
resources.addAll(previouslySet);
}
context.setAttribute(TagLibConfiguration.TLD_RESOURCES, resources);
}
}
super.preConfigure(context);
}
/**
* @param requireTldBundle The comma separated list of bundles' symbolic names
* that contain tld for this osgi webapp.
* @return The collection of jars or folders that match those bundles.
*/
private Collection<Resource> getRequireTldBundleAsJettyResources(
WebAppContext context, String requireTldBundle)
{
Bundle bundle = (Bundle)
context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
PackageAdmin packAdmin = getBundleAdmin();
String[] symbNames = requireTldBundle.split(", ");
Collection<Resource> tlds = new LinkedHashSet<Resource>();
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.getSymbolicName());
}
//take the first one as it is the most recent version?
Enumeration<URL> en = bs[0].findEntries("META-INF", "*.tld", false);
boolean atLeastOneTldFound = false;
while (en.hasMoreElements())
{
atLeastOneTldFound = true;
URL oriUrl = en.nextElement();
URL url = DefaultFileLocatorHelper.getLocalURL(oriUrl);
Resource tldResource;
try
{
tldResource = Resource.newResource(url);
}
catch (IOException e)
{
throw new IllegalArgumentException("Unable to locate the "
+ "tld resource in '"
+ url.toString()
+ "' in the bundle '" + bs[0].getSymbolicName()
+ "' while registering the "
+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE
+ " of the manifest of "
+ bundle.getSymbolicName(), e);
}
tlds.add(tldResource);
}
if (!atLeastOneTldFound)
{
Log.warn("No '/META-INF/*.tld' resources were found "
+ " in the bundle '" + bs[0].getSymbolicName()
+ "' while registering the "
+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE
+ " of the manifest of "
+ bundle.getSymbolicName());
}
}
return tlds;
}
private PackageAdmin getBundleAdmin()
{
if (packageAdminServiceTracker == null)
{
Bundle bootBundle = ((BundleReference)OSGiWebappConstants.class.getClassLoader()).getBundle();
packageAdminServiceTracker = new ServiceTracker(bootBundle.getBundleContext(),
PackageAdmin.class.getName(), null);
packageAdminServiceTracker.open();
}
return (PackageAdmin) packageAdminServiceTracker.getService();
}
}

View File

@ -16,7 +16,6 @@ package org.eclipse.jetty.osgi.boot;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Properties;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.JettyServerServiceTracker;
@ -212,6 +211,10 @@ public class JettyBootstrapActivator implements BundleActivator
Dictionary dic = new Hashtable();
dic.put(OSGiWebappConstants.SERVICE_PROP_WAR,webappFolderPath);
dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH,contextPath);
String requireTldBundle = (String)contributor.getHeaders().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
if (requireTldBundle != null) {
dic.put(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE, requireTldBundle);
}
contributor.getBundleContext().registerService(ContextHandler.class.getName(),contextHandler,dic);
}

View File

@ -36,6 +36,10 @@ public class OSGiWebappConstants
/** Name of the servlet context attribute that points to the bundle context. */
public static final String RFC66_OSGI_BUNDLE_CONTEXT = "osgi-bundlecontext";
/** Name of the servlet context attribute that points to the bundle object.
* We can't always rely on the bundle-context as there might be no such thing. */
public static final String JETTY_OSGI_BUNDLE = "osgi-bundle";
/** List of relative pathes within the bundle to the jetty context files. */
public static final String JETTY_CONTEXT_FILE_PATH = "Jetty-ContextFilePath";
@ -66,5 +70,15 @@ public class OSGiWebappConstants
* location if not null useful to install webapps or jetty context files
* that are in fact not embedded in a bundle
*/
public static final String SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE = "thisBundleInstall";
public static final String SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE = "thisBundleInstall";
/**
* Comma separated list of bundles that contain tld file used by the webapp.
*/
public static final String REQUIRE_TLD_BUNDLE = "Require-TldBundle";
/**
* Comma separated list of bundles that contain tld file used by the webapp.
* Both the name of the manifest header and the name of the service property.
*/
public static final String SERVICE_PROP_REQUIRE_TLD_BUNDLE = REQUIRE_TLD_BUNDLE;
}

View File

@ -42,6 +42,8 @@ public interface IWebBundleDeployerHelper {
* 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
@ -50,7 +52,8 @@ public interface IWebBundleDeployerHelper {
*/
public abstract WebAppContext registerWebapplication(Bundle bundle,
String webappFolderPath, String contextPath, String extraClasspath,
String overrideBundleInstallLocation, String webXmlPath,
String overrideBundleInstallLocation,
String requireTldBundle, String webXmlPath,
String defaultWebXmlPath, WebAppContext webAppContext) throws Exception;
/**
@ -72,6 +75,7 @@ public interface IWebBundleDeployerHelper {
* @param contextFileRelativePath
* @param extraClasspath
* @param overrideBundleInstallLocation
* @param requireTldBundle The list of bundles'symbolic name that contain tld files for this webapp.
* @param handler the context handler passed in the server
* reference that will be configured, deployed and started.
* @return The contexthandler created and started
@ -79,6 +83,7 @@ public interface IWebBundleDeployerHelper {
*/
public abstract ContextHandler registerContext(Bundle contributor,
String contextFileRelativePath, String extraClasspath,
String overrideBundleInstallLocation, ContextHandler handler) throws Exception;
String overrideBundleInstallLocation, String requireTldBundle,
ContextHandler handler) throws Exception;
}

View File

@ -208,9 +208,10 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
else
{
WebAppContext handler = deployerHelper
.registerWebapplication(contributor,war,contextPath,(String)sr
.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH),(String)sr
.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE),
.registerWebapplication(contributor,war,contextPath,
(String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH),
(String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE),
(String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE),
webXmlPath,defaultWebXmlPath,webapp);
if (handler != null)
{
@ -247,6 +248,7 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
ContextHandler handler = deployerHelper.registerContext(contributor,contextFilePath,
(String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH),
(String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE),
(String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE),
contextHandler);
if (handler != null)
{

View File

@ -136,11 +136,31 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
}
}
/* (non-Javadoc)
* @see org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#registerWebapplication(org.osgi.framework.Bundle, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
/**
* 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 webXmlPath, String defaultWebXmlPath, WebAppContext webAppContext) 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);
@ -179,7 +199,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
baseWebappInstallURL = webapp.toURI().toURL();
}
return registerWebapplication(bundle,webappFolderPath,baseWebappInstallURL,contextPath,
extraClasspath,bundleInstall,webXmlPath,defaultWebXmlPath,webAppContext);
extraClasspath,bundleInstall,requireTldBundle,webXmlPath,defaultWebXmlPath,webAppContext);
}
/* (non-Javadoc)
@ -187,7 +207,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
*/
private WebAppContext registerWebapplication(Bundle contributor, String pathInBundleToWebApp,
URL baseWebappInstallURL, String contextPath, String extraClasspath, File bundleInstall,
String webXmlPath, String defaultWebXmlPath, WebAppContext context) throws Exception
String requireTldBundle, String webXmlPath, String defaultWebXmlPath, WebAppContext context)
throws Exception
{
ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
@ -248,7 +269,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
//other parameters that might be defines on the OSGiAppProvider:
context.setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority());
configureWebAppContext(context,contributor);
configureWebAppContext(context,contributor,requireTldBundle);
configureWebappClassLoader(contributor,context,composite);
// @see
@ -286,7 +307,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
* @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, ContextHandler handler)
String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler)
throws Exception
{
File contextsHome = _wrapper.getOSGiAppProvider().getContextXmlDirAsFile();
@ -296,7 +317,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
if (prodContextFile.exists())
{
return registerContext(contributor,contextFileRelativePath,prodContextFile,extraClasspath,
overrideBundleInstallLocation,handler);
overrideBundleInstallLocation,requireTldBundle,handler);
}
}
File rootFolder = overrideBundleInstallLocation != null
@ -305,7 +326,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
File contextFile = rootFolder != null?new File(rootFolder,contextFileRelativePath):null;
if (contextFile != null && contextFile.exists())
{
return registerContext(contributor,contextFileRelativePath,contextFile,extraClasspath,overrideBundleInstallLocation,handler);
return registerContext(contributor,contextFileRelativePath,contextFile,extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler);
}
else
{
@ -321,7 +342,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
URL contextURL = contributor.getEntry(contextFileRelativePath);
if (contextURL != null)
{
return registerContext(contributor,contextFileRelativePath,contextURL.openStream(),extraClasspath,overrideBundleInstallLocation,handler);
return registerContext(contributor,contextFileRelativePath,contextURL.openStream(),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:""));
@ -339,13 +360,15 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
* @throws Exception
*/
private ContextHandler registerContext(Bundle contributor, String pathInBundle, File contextFile,
String extraClasspath, String overrideBundleInstallLocation, ContextHandler handler) throws Exception
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, handler);
return registerContext(contributor, pathInBundle, contextFileInputStream,
extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler);
}
finally
{
@ -360,8 +383,10 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
* happen.
* @throws Exception
*/
private ContextHandler registerContext(Bundle contributor, String pathInsideBundle, InputStream contextFileInputStream,
String extraClasspath, String overrideBundleInstallLocation, ContextHandler handler)
private ContextHandler registerContext(Bundle contributor,
String pathInsideBundle, InputStream contextFileInputStream,
String extraClasspath, String overrideBundleInstallLocation,
String requireTldBundle, ContextHandler handler)
throws Exception
{
ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
@ -376,7 +401,9 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
// classes
// that the contributor gives access to.
Thread.currentThread().setContextClassLoader(composite);
ContextHandler context = createContextHandler(handler, contributor,contextFileInputStream,extraClasspath,overrideBundleInstallLocation);
ContextHandler context = createContextHandler(handler, contributor,
contextFileInputStream,extraClasspath,
overrideBundleInstallLocation,requireTldBundle);
if (context == null)
{
return null;// did not happen
@ -417,7 +444,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
* @see {WebAppDeployer#scan} around the comment
* <code>// configure it</code>
*/
protected void configureWebAppContext(WebAppContext wah, Bundle contributor)
protected void configureWebAppContext(ContextHandler wah, Bundle contributor,
String requireTldBundle)
{
// rfc66
wah.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT,contributor.getBundleContext());
@ -429,6 +457,15 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
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);
}
/**
@ -437,11 +474,14 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
* @return
*/
protected ContextHandler createContextHandler(ContextHandler handlerToConfigure,
Bundle bundle, File contextFile, String extraClasspath, String overrideBundleInstallLocation)
Bundle bundle, File contextFile, String extraClasspath,
String overrideBundleInstallLocation, String requireTldBundle)
{
try
{
return createContextHandler(handlerToConfigure,bundle,new BufferedInputStream(new FileInputStream(contextFile)),extraClasspath,overrideBundleInstallLocation);
return createContextHandler(handlerToConfigure,bundle,
new BufferedInputStream(new FileInputStream(contextFile)),
extraClasspath,overrideBundleInstallLocation,requireTldBundle);
}
catch (FileNotFoundException e)
{
@ -457,7 +497,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
*/
@SuppressWarnings("unchecked")
protected ContextHandler createContextHandler(ContextHandler handlerToConfigure,
Bundle bundle, InputStream contextInputStream, String extraClasspath, String overrideBundleInstallLocation)
Bundle bundle, InputStream contextInputStream, String extraClasspath,
String overrideBundleInstallLocation, String requireTldBundle)
{
/*
* Do something identical to what the ContextProvider would have done:
@ -501,15 +542,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
}
}
// rfc-66:
context.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:
context.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(),
bundle.getBundleContext());
configureWebAppContext(context, bundle, requireTldBundle);
return context;
}
catch (FileNotFoundException e)