blob: e55a03aea5b32dfba43e6c031777ced6d011ef89 [file] [log] [blame]
// ========================================================================
// 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
// 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.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.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.ContextDeployer;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.WebAppDeployer;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.internal.jsp.TldLocatableURLClassloader;
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.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* Bridges the jetty deployers with the OSGi lifecycle where applications are
* managed inside OSGi-bundles.
* <p>
* This class should be called as a consequence of the activation of a new
* service that is a ContextHandler.<br/>
* This way the new webapps are exposed as OSGi services.
* </p>
* <p>
* Helper methods to register a bundle that is a web-application or a context.
* </p>
* Limitations:
* <ul>
* <li>support for jarred webapps is somewhat limited.</li>
* </ul>
*/
public class WebappRegistrationHelper
{
private static Logger __logger = Log.getLogger(WebappRegistrationHelper.class.getName());
private static boolean INITIALIZED = false;
/**
* By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
* equinox and apache-felix fragment bundles that are specific to an OSGi
* implementation should set a different implementation.
*/
public static BundleClassLoaderHelper BUNDLE_CLASS_LOADER_HELPER = null;
/**
* By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
* equinox and apache-felix fragment bundles that are specific to an OSGi
* implementation should set a different implementation.
*/
public static BundleFileLocatorHelper BUNDLE_FILE_LOCATOR_HELPER = null;
/**
* By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
* equinox and apache-felix fragment bundles that are specific to an OSGi
* implementation should set a different implementation.
* <p>
* Several of those objects can be added here: For example we could have an optional fragment that setups
* a specific implementation of JSF for the whole of jetty-osgi.
* </p>
*/
public static Collection<WebappRegistrationCustomizer> JSP_REGISTRATION_HELPERS = new ArrayList<WebappRegistrationCustomizer>();
private Server _server;
private ContextHandlerCollection _ctxtHandler;
/**
* 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;
/**
* This is the class loader that should be the parent classloader of any
* webapp classloader. It is in fact the _libExtClassLoader with a trick to
* let the TldScanner find the jars where the tld files are.
*/
private URLClassLoader _commonParentClassLoaderForWebapps;
private DeploymentManager _deploymentManager;
private OSGiAppProvider _provider;
public WebappRegistrationHelper(Server server)
{
_server = server;
staticInit();
}
// Inject the customizing classes that might be defined in fragment bundles.
private 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();
}
}
}
/**
* Removes quotes around system property values before we try to make them
* into file pathes.
*/
public static String stripQuotesIfPresent(String filePath)
{
if (filePath == null)
return null;
if ((filePath.startsWith("\"") || filePath.startsWith("'")) && (filePath.endsWith("\"") || filePath.endsWith("'")))
return filePath.substring(1,filePath.length() - 1);
return filePath;
}
/**
* Look for the home directory of jetty as defined by the system property
* 'jetty.home'. If undefined, look at the current bundle and uses its own
* jettyhome folder for this feature.
* <p>
* Special case: inside eclipse-SDK:<br/>
* If the bundle is jarred, see if we are inside eclipse-PDE itself. In that
* case, look for the installation directory of eclipse-PDE, try to create a
* jettyhome folder there and install the sample jettyhome folder at that
* location. This makes the installation in eclipse-SDK easier. <br/>
* This is a bit redundant with the work done by the jetty configuration
* launcher.
* </p>
*
* @param context
* @throws Exception
*/
public void setup(BundleContext context, Map<String, String> configProperties) throws Exception
{
File _installLocation = BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(context.getBundle());
// debug:
// new File("~/proj/eclipse-install/eclipse-3.5.1-SDK-jetty7/" +
// "dropins/jetty7/plugins/org.eclipse.jetty.osgi.boot_0.0.1.001-SNAPSHOT.jar");
boolean bootBundleCanBeJarred = true;
String jettyHome = stripQuotesIfPresent(System.getProperty("jetty.home"));
if (jettyHome == null || jettyHome.length() == 0)
{
if (_installLocation.getName().endsWith(".jar"))
{
jettyHome = JettyHomeHelper.setupJettyHomeInEclipsePDE(_installLocation);
}
if (jettyHome == null)
{
jettyHome = _installLocation.getAbsolutePath() + "/jettyhome";
bootBundleCanBeJarred = false;
}
}
// in case we stripped the quotes.
System.setProperty("jetty.home",jettyHome);
String jettyLogs = stripQuotesIfPresent(System.getProperty("jetty.logs"));
if (jettyLogs == null || jettyLogs.length() == 0)
{
System.setProperty("jetty.logs",jettyHome + "/logs");
}
if (!bootBundleCanBeJarred && !_installLocation.isDirectory())
{
String install = _installLocation != null?_installLocation.getCanonicalPath():" unresolved_install_location";
throw new IllegalArgumentException("The system property -Djetty.home" + " must be set to a directory or the bundle "
+ context.getBundle().getSymbolicName() + " installed here " + install + " must be unjarred.");
}
try
{
System.err.println("JETTY_HOME set to " + new File(jettyHome).getCanonicalPath());
}
catch (Throwable t)
{
System.err.println("JETTY_HOME _set to " + new File(jettyHome).getAbsolutePath());
}
ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
try
{
// passing this bundle's classloader as the context classlaoder
// makes sure there is access to all the jetty's bundles
File jettyHomeF = new File(jettyHome);
URLClassLoader libExtClassLoader = null;
try
{
libExtClassLoader = LibExtClassLoaderHelper.createLibEtcClassLoaderHelper(jettyHomeF,_server,
JettyBootstrapActivator.class.getClassLoader());
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
Thread.currentThread().setContextClassLoader(libExtClassLoader);
String jettyetc = System.getProperty(OSGiWebappConstants.SYS_PROP_JETTY_ETC_FILES,"etc/jetty.xml");
StringTokenizer tokenizer = new StringTokenizer(jettyetc,";,");
Map<Object,Object> id_map = new HashMap<Object,Object>();
id_map.put("Server",_server);
Map<Object,Object> properties = new HashMap<Object,Object>();
properties.put("jetty.home",jettyHome);
properties.put("jetty.host",System.getProperty("jetty.host",""));
properties.put("jetty.port",System.getProperty("jetty.port","8080"));
properties.put("jetty.port.ssl",System.getProperty("jetty.port.ssl","8443"));
while (tokenizer.hasMoreTokens())
{
String etcFile = tokenizer.nextToken().trim();
File conffile = etcFile.startsWith("/")?new File(etcFile):new File(jettyHomeF,etcFile);
if (!conffile.exists())
{
__logger.warn("Unable to resolve the jetty/etc file " + etcFile);
if ("etc/jetty.xml".equals(etcFile))
{
// Missing jetty.xml file, so create a minimal Jetty configuration
__logger.info("Configuring default server on 8080");
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(8080);
_server.addConnector(connector);
HandlerCollection handlers = new HandlerCollection();
ContextHandlerCollection contexts = new ContextHandlerCollection();
RequestLogHandler requestLogHandler = new RequestLogHandler();
handlers.setHandlers(new Handler[] { contexts, new DefaultHandler(), requestLogHandler });
_server.setHandler(handlers);
}
}
else
{
try
{
// Execute a Jetty configuration file
XmlConfiguration config = new XmlConfiguration(new FileInputStream(conffile));
config.setIdMap(id_map);
config.setProperties(properties);
config.configure();
id_map=config.getIdMap();
}
catch (SAXParseException saxparse)
{
Log.getLogger(WebappRegistrationHelper.class.getName()).warn("Unable to configure the jetty/etc file " + etcFile,saxparse);
throw saxparse;
}
}
}
init();
//now that we have an app provider we can call the registration customizer.
try
{
URL[] jarsWithTlds = getJarsWithTlds();
_commonParentClassLoaderForWebapps = jarsWithTlds == null?libExtClassLoader:new TldLocatableURLClassloader(libExtClassLoader,getJarsWithTlds());
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
_server.start();
}
catch (Throwable t)
{
t.printStackTrace();
}
finally
{
Thread.currentThread().setContextClassLoader(contextCl);
}
}
/**
* Must be called after the server is configured.
*
* Locate the actual instance of the ContextDeployer and WebAppDeployer that
* was created when configuring the server through jetty.xml. If there is no
* such thing it won't be possible to deploy webapps from a context and we
* throw IllegalStateExceptions.
*/
private void init()
{
// Get the context handler
_ctxtHandler = (ContextHandlerCollection)_server.getChildHandlerByClass(ContextHandlerCollection.class);
// get a deployerManager
List<DeploymentManager> deployers = _server.getBeans(DeploymentManager.class);
if (deployers != null && !deployers.isEmpty())
{
_deploymentManager = deployers.get(0);
for (AppProvider provider : _deploymentManager.getAppProviders())
{
if (provider instanceof OSGiAppProvider)
{
_provider=(OSGiAppProvider)provider;
break;
}
}
if (_provider == null)
{
//create it on the fly with reasonable default values.
try
{
_provider = new OSGiAppProvider();
_provider.setMonitoredDir(
Resource.newResource(getDefaultOSGiContextsHome(
new File(System.getProperty("jetty.home"))).toURI()));
} catch (IOException e) {
e.printStackTrace();
}
_deploymentManager.addAppProvider(_provider);
}
}
if (_ctxtHandler == null || _provider==null)
throw new IllegalStateException("ERROR: No ContextHandlerCollection or OSGiAppProvider configured");
}
/**
* 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 webXmlPath
* @param defaultWebXmlPath
* TODO: parameter description
* @return The contexthandler created and started
* @throws Exception
*/
public ContextHandler registerWebapplication(Bundle bundle, String webappFolderPath, String contextPath, String extraClasspath,
String overrideBundleInstallLocation, String webXmlPath, String defaultWebXmlPath) throws Exception
{
File bundleInstall = overrideBundleInstallLocation == null?BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(bundle):new File(
overrideBundleInstallLocation);
File webapp = null;
if (webappFolderPath != null && webappFolderPath.length() != 0 && !webappFolderPath.equals("."))
{
if (webappFolderPath.startsWith("/") || webappFolderPath.startsWith("file:/"))
{
webapp = new File(webappFolderPath);
}
else
{
webapp = new File(bundleInstall,webappFolderPath);
}
}
else
{
webapp = bundleInstall;
}
if (!webapp.exists())
{
throw new IllegalArgumentException("Unable to locate " + webappFolderPath + " inside "
+ (bundleInstall != null?bundleInstall.getAbsolutePath():"unlocated bundle '" + bundle.getSymbolicName() + "'"));
}
return registerWebapplication(bundle,webapp,contextPath,extraClasspath,bundleInstall,webXmlPath,defaultWebXmlPath);
}
/**
* TODO: refactor this into the createContext method of OSGiAppProvider.
* @see WebAppDeployer#scan()
* @param contributor
* @param webapp
* @param contextPath
* @param extraClasspath
* @param bundleInstall
* @param webXmlPath
* @param defaultWebXmlPath
* @return The contexthandler created and started
* @throws Exception
*/
public ContextHandler registerWebapplication(Bundle contributor, File webapp, String contextPath, String extraClasspath, File bundleInstall,
String webXmlPath, String defaultWebXmlPath) throws Exception
{
ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
String[] oldServerClasses = null;
WebAppContext context = 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);
context = new WebAppContext(webapp.getAbsolutePath(),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 = _provider.getDefaultsDescriptor();
}
if (defaultWebXmlPath != null && defaultWebXmlPath.length() != 0)
{
File defaultWebXml = null;
if (defaultWebXmlPath.startsWith("/") || defaultWebXmlPath.startsWith("file:/"))
{
defaultWebXml = new File(webXmlPath);
}
else
{
defaultWebXml = new File(bundleInstall,defaultWebXmlPath);
}
if (defaultWebXml.exists())
{
context.setDefaultsDescriptor(defaultWebXml.getAbsolutePath());
}
}
//other parameters that might be defines on the OSGiAppProvider:
context.setParentLoaderPriority(_provider.isParentLoaderPriority());
configureWebAppContext(context,contributor);
configureWebappClassLoader(contributor,context,composite);
// @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);
_provider.addContext(context);
return context;
}
finally
{
if (context != null && oldServerClasses != null)
{
context.setServerClasses(oldServerClasses);
}
Thread.currentThread().setContextClassLoader(contextCl);
}
}
/**
* Stop a ContextHandler and remove it from the collection.
*
* @see ContextDeployer#undeploy
* @param contextHandler
* @throws Exception
*/
public void unregister(ContextHandler contextHandler) throws Exception
{
contextHandler.stop();
_ctxtHandler.removeHandler(contextHandler);
}
/**
* @return The default folder in which the context files of the osgi bundles
* are located and watched. Or null when the system property
* "jetty.osgi.contexts.home" is not defined.
* If the configuration file defines the OSGiAppProvider's context.
* This will not be taken into account.
*/
File getDefaultOSGiContextsHome(File jettyHome)
{
String jettyContextsHome = System.getProperty("jetty.osgi.contexts.home");
if (jettyContextsHome != null)
{
File contextsHome = new File(jettyContextsHome);
if (!contextsHome.exists() || !contextsHome.isDirectory())
{
throw new IllegalArgumentException("the ${jetty.osgi.contexts.home} '" + jettyContextsHome + " must exist and be a folder");
}
return contextsHome;
}
return new File(jettyHome, "/contexts");
}
File getOSGiContextsHome()
{
return _provider.getContextXmlDirAsFile();
}
/**
* This type of registration relies on jetty's complete context xml file.
* Context encompasses jndi and all other things. This makes the definition
* of the webapp a lot more self-contained.
*
* @param contributor
* @param contextFileRelativePath
* @param extraClasspath
* @param overrideBundleInstallLocation
* @return The contexthandler created and started
* @throws Exception
*/
public ContextHandler registerContext(Bundle contributor, String contextFileRelativePath, String extraClasspath, String overrideBundleInstallLocation)
throws Exception
{
File contextsHome = _provider.getContextXmlDirAsFile();
if (contextsHome != null)
{
File prodContextFile = new File(contextsHome,contributor.getSymbolicName() + "/" + contextFileRelativePath);
if (prodContextFile.exists())
{
return registerContext(contributor,prodContextFile,extraClasspath,overrideBundleInstallLocation);
}
}
File contextFile = overrideBundleInstallLocation != null?new File(overrideBundleInstallLocation,contextFileRelativePath):new File(
BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(contributor),contextFileRelativePath);
if (contextFile.exists())
{
return registerContext(contributor,contextFile,extraClasspath,overrideBundleInstallLocation);
}
else
{
if (contextFileRelativePath.startsWith("./"))
{
contextFileRelativePath = contextFileRelativePath.substring(1);
}
if (!contextFileRelativePath.startsWith("/"))
{
contextFileRelativePath = "/" + contextFileRelativePath;
}
if (overrideBundleInstallLocation == null)
{
URL contextURL = contributor.getEntry(contextFileRelativePath);
if (contextURL != null)
{
return registerContext(contributor,contextURL.openStream(),extraClasspath,overrideBundleInstallLocation);
}
}
else
{
JarFile zipFile = null;
try
{
zipFile = new JarFile(overrideBundleInstallLocation);
ZipEntry entry = zipFile.getEntry(contextFileRelativePath.substring(1));
return registerContext(contributor,zipFile.getInputStream(entry),extraClasspath,overrideBundleInstallLocation);
}
catch (Throwable t)
{
}
finally
{
if (zipFile != null)
try
{
zipFile.close();
}
catch (IOException ioe)
{
}
}
}
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, File contextFile, String extraClasspath, String overrideBundleInstallLocation) throws Exception
{
InputStream contextFileInputStream = null;
try
{
contextFileInputStream = new BufferedInputStream(new FileInputStream(contextFile));
return registerContext(contributor,contextFileInputStream,extraClasspath,overrideBundleInstallLocation);
}
finally
{
if (contextFileInputStream != null)
try
{
contextFileInputStream.close();
}
catch (IOException ioe)
{
}
}
}
/**
* @param contributor
* @param contextFileInputStream
* @return The ContextHandler created and registered or null if it did not
* happen.
* @throws Exception
*/
private ContextHandler registerContext(Bundle contributor, InputStream contextFileInputStream, String extraClasspath, String overrideBundleInstallLocation)
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(contributor,contextFileInputStream,extraClasspath,overrideBundleInstallLocation);
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);
if (context instanceof WebAppContext)
{
webAppContext = (WebAppContext)context;
// @see
// org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext)
oldServerClasses = webAppContext.getServerClasses();
webAppContext.setServerClasses(null);
}
_provider.addContext(context);
return context;
}
finally
{
if (webAppContext != null)
{
webAppContext.setServerClasses(oldServerClasses);
}
Thread.currentThread().setContextClassLoader(contextCl);
}
}
/**
* TODO: right now only the jetty-jsp bundle is scanned for common taglibs.
* Should support a way to plug more bundles that contain taglibs.
*
* The jasper TldScanner expects a URLClassloader to parse a jar for the
* /META-INF/*.tld it may contain. We place the bundles that we know contain
* such tag-libraries. Please note that it will work if and only if the
* bundle is a jar (!) Currently we just hardcode the bundle that contains
* the jstl implemenation.
*
* A workaround when the tld cannot be parsed with this method is to copy
* and paste it inside the WEB-INF of the webapplication where it is used.
*
* Support only 2 types of packaging for the bundle: - the bundle is a jar
* (recommended for runtime.) - the bundle is a folder and contain jars in
* the root and/or in the lib folder (nice for PDE developement situations)
* Unsupported: the bundle is a jar that embeds more jars.
*
* @return
* @throws Exception
*/
private URL[] getJarsWithTlds() throws Exception
{
ArrayList<URL> res = new ArrayList<URL>();
for (WebappRegistrationCustomizer regCustomizer : JSP_REGISTRATION_HELPERS)
{
URL[] urls = regCustomizer.getJarsWithTlds(_provider, BUNDLE_FILE_LOCATOR_HELPER);
for (URL url : urls)
{
if (!res.contains(url))
res.add(url);
}
}
if (!res.isEmpty())
return res.toArray(new URL[res.size()]);
else
return null;
}
/**
* Applies the properties of WebAppDeployer as defined in jetty.xml.
*
* @see {WebAppDeployer#scan} around the comment
* <code>// configure it</code>
*/
protected void configureWebAppContext(WebAppContext wah, Bundle contributor)
{
// 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());
}
/**
* @See {@link ContextDeployer#scan}
* @param contextFile
* @return
*/
protected ContextHandler createContextHandler(Bundle bundle, File contextFile, String extraClasspath, String overrideBundleInstallLocation)
{
try
{
return createContextHandler(bundle,new BufferedInputStream(new FileInputStream(contextFile)),extraClasspath,overrideBundleInstallLocation);
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
return null;
}
/**
* @See {@link ContextDeployer#scan}
* @param contextFile
* @return
*/
@SuppressWarnings("unchecked")
protected ContextHandler createContextHandler(Bundle bundle, InputStream contextInputStream, String extraClasspath, String overrideBundleInstallLocation)
{
/*
* 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",_server);
// insert the bundle's location as a property.
setThisBundleHomeProperty(bundle,properties,overrideBundleInstallLocation);
xmlConfiguration.setProperties(properties);
ContextHandler context = (ContextHandler)xmlConfiguration.configure();
if (context instanceof WebAppContext)
{
((WebAppContext)context).setExtraClasspath(extraClasspath);
((WebAppContext)context).setParentLoaderPriority(_provider.isParentLoaderPriority());
if (_provider.getDefaultsDescriptor() != null && _provider.getDefaultsDescriptor().length() != 0)
{
((WebAppContext)context).setDefaultsDescriptor(_provider.getDefaultsDescriptor());
}
}
// 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());
return context;
}
catch (FileNotFoundException e)
{
return null;
}
catch (SAXException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (Throwable e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
finally
{
if (contextInputStream != null)
try
{
contextInputStream.close();
}
catch (IOException ioe)
{
}
}
return null;
}
/**
* Configure a classloader onto the context. If the context is a
* WebAppContext, build a WebAppClassLoader that has access to all the jetty
* classes thanks to the classloader of the JettyBootStrapper bundle and
* also has access to the classloader of the bundle that defines this
* context.
* <p>
* If the context is not a WebAppContext, same but with a simpler
* URLClassLoader. Note that the URLClassLoader is pretty much fake: it
* delegate all actual classloading to the parent classloaders.
* </p>
* <p>
* The URL[] returned by the URLClassLoader create contained specifically
* the jars that some j2ee tools expect and look into. For example the jars
* that contain tld files for jasper's jstl support.
* </p>
* <p>
* Also as the jars in the lib folder and the classes in the classes folder
* might already be in the OSGi classloader we filter them out of the
* WebAppClassLoader
* </p>
*
* @param context
* @param contributor
* @param webapp
* @param contextPath
* @param classInBundle
* @throws Exception
*/
protected void configureWebappClassLoader(Bundle contributor, ContextHandler context, OSGiWebappClassLoader webappClassLoader) throws Exception
{
if (context instanceof WebAppContext)
{
WebAppContext webappCtxt = (WebAppContext)context;
context.setClassLoader(webappClassLoader);
webappClassLoader.setWebappContext(webappCtxt);
}
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(_commonParentClassLoaderForWebapps,new WebAppContext(),contributor);
return webappClassLoader;
}
/**
* Set the property &quot;this.bundle.install&quot; to point to the location
* of the bundle. Useful when <SystemProperty name="this.bundle.home"/> is
* used.
*/
private void setThisBundleHomeProperty(Bundle bundle, HashMap<String, Object> properties, String overrideBundleInstallLocation)
{
try
{
File location = overrideBundleInstallLocation != null?new File(overrideBundleInstallLocation):BUNDLE_FILE_LOCATOR_HELPER
.getBundleInstallLocation(bundle);
properties.put("this.bundle.install",location.getCanonicalPath());
}
catch (Throwable t)
{
System.err.println("Unable to set 'this.bundle.install' " + " for the bundle " + bundle.getSymbolicName());
t.printStackTrace();
}
}
}