Merge branch 'master' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project

This commit is contained in:
Greg Wilkins 2012-07-23 14:45:15 +10:00
commit dc7ae977ae
59 changed files with 4521 additions and 2234 deletions

View File

@ -0,0 +1,145 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Jetty Project
Contributor License Agreement V1.0
based on http://www.apache.org/licenses/
Thank you for your interest in the Jetty project by Mort Bay
Consulting Pty. Ltd. Australia ("MortBay").
In order to clarify the intellectual property license
granted with Contributions from any person or entity, MortBay
must have a Contributor License Agreement ("CLA") that has
been signed by each Contributor, indicating agreement to the license
terms below. This license is for your protection as a Contributor as
well as the protection of MortBay and its users; it does not
change your rights to use your own Contributions for any other
purpose.
If you have not already done so, please complete this agreement
and commit it to the Jetty repository at
svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty
at legal/cla-USERNAME.txt using your authenticated codehaus ssh
login. If you do not have commit privilege to the repository, please
email the file to eclipse@eclipse.com. If possible, digitally sign
the committed file, otherwise also send a signed Agreement to MortBay.
Please read this document carefully before signing and keep a copy for
your records.
Full name: Thomas Becker
E-Mail: thomas.becker00@googlemail.com
Mailing Address:
You accept and agree to the following terms and conditions for Your
present and future Contributions submitted to MortBay. In return,
MortBay shall not use Your Contributions in a way that is contrary
to the software license in effect at the time of the Contribution.
Except for the license granted herein to MortBay and recipients of
software distributed by MortBay, You reserve all right, title, and
interest in and to Your Contributions.
1. Definitions.
"You" (or "Your") shall mean the copyright owner or legal entity
authorized by the copyright owner that is making this Agreement
with MortBay. For legal entities, the entity making a
Contribution and all other entities that control, are controlled
by, or are under common control with that entity are considered to
be a single Contributor. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"Contribution" shall mean any original work of authorship,
including any modifications or additions to an existing work, that
is intentionally submitted by You to MortBay for inclusion
in, or documentation of, any of the products owned or managed by
MortBay (the "Work"). For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written
communication sent to MortBay or its representatives,
including but not limited to communication on electronic mailing
lists, source code control systems, and issue tracking systems that
are managed by, or on behalf of, MortBay for the purpose of
discussing and improving the Work, but excluding communication that
is conspicuously marked or otherwise designated in writing by You
as "Not a Contribution."
2. Grant of Copyright License. Subject to the terms and conditions of
this Agreement, You hereby grant to MortBay and to
recipients of software distributed by MortBay a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare derivative works of,
publicly display, publicly perform, sublicense, and distribute Your
Contributions and such derivative works.
3. Grant of Patent License. Subject to the terms and conditions of
this Agreement, You hereby grant to MortBay and to
recipients of software distributed by MortBay a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the
Work, where such license applies only to those patent claims
licensable by You that are necessarily infringed by Your
Contribution(s) alone or by combination of Your Contribution(s)
with the Work to which such Contribution(s) was submitted. If any
entity institutes patent litigation against You or any other entity
(including a cross-claim or counterclaim in a lawsuit) alleging
that your Contribution, or the Work to which you have contributed,
constitutes direct or contributory patent infringement, then any
patent licenses granted to that entity under this Agreement for
that Contribution or Work shall terminate as of the date such
litigation is filed.
4. You represent that you are legally entitled to grant the above
license. If your employer(s) has rights to intellectual property
that you create that includes your Contributions, you represent
that you have received permission to make Contributions on behalf
of that employer, that your employer has waived such rights for
your Contributions to MortBay, or that your employer has
executed a separate Corporate CLA with MortBay.
5. You represent that each of Your Contributions is Your original
creation (see section 7 for submissions on behalf of others). You
represent that Your Contribution submissions include complete
details of any third-party license or other restriction (including,
but not limited to, related patents and trademarks) of which you
are personally aware and which are associated with any part of Your
Contributions.
6. You are not expected to provide support for Your Contributions,
except to the extent You desire to provide support. You may provide
support for free, for a fee, or not at all. Unless required by
applicable law or agreed to in writing, You provide Your
Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied, including, without
limitation, any warranties or conditions of TITLE, NON-
INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
7. Should You wish to submit work that is not Your original creation,
You may submit it to MortBay separately from any
Contribution, identifying the complete details of its source and of
any license or other restriction (including, but not limited to,
related patents, trademarks, and license agreements) of which you
are personally aware, and conspicuously marking the work as
"Submitted on behalf of a third-party: [named here]".
8. You agree to notify MortBay of any facts or circumstances of
which you become aware that would make these representations
inaccurate in any respect.
Date: 2012-07-17
Please sign:
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
iQEcBAEBAgAGBQJQBb4tAAoJEMHhjBmtgF91HDcH/2nQDPuPztWFrBifnEoLF6Jl
RUkfJzAPZaLDtDMfiDz7ucdRL1RDodmz4VIF2+fbKeBYQquZXfXIeEghz+tKriK3
0M12guFkNLDteQp9h2p3Zu9JU3K0y4m84IDWq72HRmh1nRyD6lzZFhDGZ/D+69fF
tgYG0FwEit00MAq/lRbsXHLpBOY+Jyh/Xy+QRnQTcAQ+tAgOlxds3w+JSs2sGdes
YLAJQQacLeGh7EzD3F+CKuiwT4c5ub64LdXSlAVj1u2OjZBfqLaJ3FA60Ti+I3kn
FNWKpzaeX+SQgMak6hsuatXi6EsVk6sIaskwEgl6+Xk+HYWy23ZQ8BKQRLKOZTw=
=gAqN
-----END PGP SIGNATURE-----

View File

@ -1,6 +1,6 @@
jetty-7.6.6-SNAPSHOT
jetty-7.6.5.v20120713 - 13 July 2012
jetty-7.6.5.v20120716 - 16 July 2012
+ 376717 Balancer Servlet with round robin support, contribution, added
missing license
+ 379250 Server is added to shutdown hook twice
@ -19,6 +19,8 @@ jetty-7.6.5.v20120713 - 13 July 2012
+ 383881 WebSocketHandler sets request as handled
+ 384254 revert change to writable when not dispatched
+ 384847 CrossOriginFilter is not working.
+ 384896 JDBCSessionManager fails to load existing sessions on oracle when
contextPath is /
+ 384980 Jetty client unable to recover from Time outs when connection count
per address hits max.
+ JETTY-1525 Show handle status in response debug message

View File

@ -32,10 +32,11 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.junit.Assert;
import org.junit.Test;
import org.junit.Ignore;
public class ExpirationWithLimitedConnectionsTest
{
@Test
@Ignore
public void testExpirationWithMaxConnectionPerAddressReached() throws Exception
{
final Logger logger = Log.getLogger("org.eclipse.jetty.client");

View File

@ -19,9 +19,12 @@ 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.osgi.boot.OSGiAppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
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 +59,10 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra
* @param provider
* @return
*/
private static Collection<String> getTldBundles(OSGiAppProvider provider)
private static Collection<String> 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 +86,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<URL> urls = new ArrayList<URL>();
// 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 +98,24 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra
// and mirroring those in the MANIFEST.MF
Bundle[] bundles = FrameworkUtil.getBundle(PluggableWebAppRegistrationCustomizerImpl.class).getBundleContext().getBundles();
Collection<String> tldbundles = getTldBundles(provider);
HashSet<URL> urls = new HashSet<URL>();
String tmp = System.getProperty(SYS_PROP_TLD_BUNDLES); //comma separated exact names
List<String> sysNames = new ArrayList<String>();
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 +147,7 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra
* @param urls
* @throws Exception
*/
private void registerTldBundle(BundleFileLocatorHelper locatorHelper, Bundle bundle, List<URL> urls) throws Exception
private void registerTldBundle(BundleFileLocatorHelper locatorHelper, Bundle bundle, Set<URL> urls) throws Exception
{
File jasperLocation = locatorHelper.getBundleInstallLocation(bundle);
if (jasperLocation.isDirectory())

View File

@ -25,10 +25,12 @@ 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 +48,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
@ -81,12 +85,10 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
{
// sanity check:
Class cl = getClass().getClassLoader().loadClass("org.apache.jasper.servlet.JspServlet");
// System.err.println("found the jsp servlet: " + cl.getName());
}
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 +108,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 +130,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<Class<?>> classesToAddToTheTldBundles = new HashSet<Class<?>>();

View File

@ -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.
* <p>
* 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.
* </p>
@ -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);
}
}

View File

@ -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
{

View File

@ -9,6 +9,23 @@
<Call name="addBean">
<Arg>
<New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
<Set name="useStandardBindings">false</Set>
<Set name="lifeCycleBindings">
<Array type="org.eclipse.jetty.deploy.AppLifeCycle.Binding">
<Item>
<New class="org.eclipse.jetty.osgi.boot.OSGiDeployer"/>
</Item>
<Item>
<New class="org.eclipse.jetty.deploy.bindings.StandardStarter"/>
</Item>
<Item>
<New class="org.eclipse.jetty.deploy.bindings.StandardStopper"/>
</Item>
<Item>
<New class="org.eclipse.jetty.osgi.boot.OSGiUndeployer"/>
</Item>
</Array>
</Set>
<Set name="contexts">
<Ref id="Contexts" />
</Set>
@ -17,22 +34,25 @@
<Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
</Call>
<!-- Providers of OSGi Apps -->
<Call name="addAppProvider">
<Arg>
<New class="org.eclipse.jetty.osgi.boot.OSGiAppProvider">
<!-- Call name="addAppProvider" -->
<!-- Arg -->
<!-- New class="org.eclipse.jetty.osgi.boot.OSGiAppProvider" -->
<!--
<Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
-->
<!--
<Set name="scanInterval">5</Set>
<Set name="contextXmlDir"><Property name="jetty.home" default="." />/contexts</Set>
-->
<!-- comma separated list of bundle symbolic names that contain custom tag libraries (*.tld files) -->
<!-- if those bundles don't exist or can't be loaded no errors or warning will be issued! -->
<!-- This default value plugs in the tld files of the reference implementation of JSF -->
<Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldbundles" default="javax.faces.jsf-impl" /></Set>
<!--
<Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldbundles" default="javax.faces.jsf-impl" /></Set>
</New>
</Arg>
</Call>
-->
</New>
</Arg>
</Call>

View File

@ -0,0 +1,355 @@
// ========================================================================
// 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 org.eclipse.jetty.osgi.boot;
import java.io.File;
import java.net.URL;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
import org.eclipse.jetty.osgi.boot.utils.OSGiClassLoader;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.JarResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
/**
* AbstractContextProvider
*
*
*/
public abstract class AbstractContextProvider extends AbstractLifeCycle implements AppProvider
{
private static final Logger LOG = Log.getLogger(AbstractContextProvider.class);
private DeploymentManager _deploymentManager;
private ServerInstanceWrapper _serverWrapper;
/* ------------------------------------------------------------ */
/**
* BundleApp
*
*
*/
public class OSGiApp extends AbstractOSGiApp
{
private String _contextFile;
private ContextHandler _contextHandler;
private boolean _configured = false;
public OSGiApp(DeploymentManager manager, AppProvider provider, String originId, Bundle bundle, String contextFile)
{
super(manager, provider, bundle, originId);
_contextFile = contextFile;
}
public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String contextFile, String originId)
{
super(manager, provider, bundle, properties, originId);
_contextFile = contextFile;
}
public String getContextFile ()
{
return _contextFile;
}
public void setHandler(ContextHandler h)
{
_contextHandler = h;
}
public ContextHandler createContextHandler()
throws Exception
{
configureContextHandler();
return _contextHandler;
}
public void configureContextHandler()
throws Exception
{
if (_configured)
return;
_configured = true;
//Override for bundle root may have been set
String bundleOverrideLocation = (String)_properties.get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
if (bundleOverrideLocation == null)
bundleOverrideLocation = (String)_properties.get(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE);
//Location on filesystem of bundle or the bundle override location
File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(_bundle);
File root = (bundleOverrideLocation==null?bundleLocation:new File(bundleOverrideLocation));
Resource rootResource = Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(root.toURI().toURL()));
//try and make sure the rootResource is useable - if its a jar then make it a jar file url
if (rootResource.exists()&& !rootResource.isDirectory() && !rootResource.toString().startsWith("jar:"))
{
Resource jarResource = JarResource.newJarResource(rootResource);
if (jarResource.exists() && jarResource.isDirectory())
rootResource = jarResource;
}
//Set the base resource of the ContextHandler, if not already set, can also be overridden by the context xml file
if (_contextHandler != null && _contextHandler.getBaseResource() == null)
{
_contextHandler.setBaseResource(rootResource);
}
//Use a classloader that knows about the common jetty parent loader, and also the bundle
OSGiClassLoader classLoader = new OSGiClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps(), _bundle);
//if there is a context file, find it and apply it
if (_contextFile == null && _contextHandler == null)
throw new IllegalStateException("No context file or ContextHandler");
if (_contextFile != null)
{
//apply the contextFile, creating the ContextHandler, the DeploymentManager will register it in the ContextHandlerCollection
Resource res = null;
//try to find the context file in the filesystem
if (_contextFile.startsWith("/"))
res = getFileAsResource(_contextFile);
//try to find it relative to jetty home
if (res == null)
{
//See if the specific server we are related to has jetty.home set
String jettyHome = (String)getServerInstanceWrapper().getServer().getAttribute(OSGiServerConstants.JETTY_HOME);
if (jettyHome != null)
res = getFileAsResource(jettyHome, _contextFile);
//try to see if a SystemProperty for jetty.home is set
if (res == null)
{
jettyHome = System.getProperty(OSGiServerConstants.JETTY_HOME);
if (jettyHome != null)
{
if (jettyHome.startsWith("\"") || jettyHome.startsWith("'"))
jettyHome = jettyHome.substring(1);
if (jettyHome.endsWith("\"") || (jettyHome.endsWith("'")))
jettyHome = jettyHome.substring(0,jettyHome.length()-1);
res = getFileAsResource(jettyHome, _contextFile);
if (LOG.isDebugEnabled()) LOG.debug("jetty home context file:"+res);
}
}
}
//try to find it relative to an override location that has been specified
if (res == null)
{
if (bundleOverrideLocation != null)
{
res = getFileAsResource(Resource.newResource(bundleOverrideLocation).getFile(), _contextFile);
if (LOG.isDebugEnabled()) LOG.debug("Bundle override location context file:"+res);
}
}
//try to find it relative to the bundle in which it is being deployed
if (res == null)
{
if (_contextFile.startsWith("./"))
_contextFile = _contextFile.substring(1);
if (!_contextFile.startsWith("/"))
_contextFile = "/" + _contextFile;
URL contextURL = _bundle.getEntry(_contextFile);
if (contextURL != null)
res = Resource.newResource(contextURL);
}
//apply the context xml file, either to an existing ContextHandler, or letting the
//it create the ContextHandler as necessary
if (res != null)
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
LOG.debug("Context classloader = " + cl);
try
{
Thread.currentThread().setContextClassLoader(classLoader);
XmlConfiguration xmlConfiguration = new XmlConfiguration(res.getInputStream());
HashMap properties = new HashMap();
//put the server instance in
properties.put("Server", getServerInstanceWrapper().getServer());
//put in the location of the bundle root
properties.put("bundle.root", rootResource.toString());
// insert the bundle's location as a property.
xmlConfiguration.getProperties().putAll(properties);
if (_contextHandler == null)
_contextHandler = (ContextHandler) xmlConfiguration.configure();
else
xmlConfiguration.configure(_contextHandler);
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
}
//Set up the class loader we created
_contextHandler.setClassLoader(classLoader);
//If a bundle/service property specifies context path, let it override the context xml
String contextPath = (String)_properties.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (contextPath == null)
contextPath = (String)_properties.get(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH);
if (contextPath != null)
_contextHandler.setContextPath(contextPath);
//osgi Enterprise Spec r4 p.427
_contextHandler.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, _bundle.getBundleContext());
//make sure we protect also the osgi dirs specified by OSGi Enterprise spec
String[] targets = _contextHandler.getProtectedTargets();
int length = (targets==null?0:targets.length);
String[] updatedTargets = null;
if (targets != null)
{
updatedTargets = new String[length+OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(targets, 0, updatedTargets, 0, length);
}
else
updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length);
_contextHandler.setProtectedTargets(updatedTargets);
}
private Resource getFileAsResource (String dir, String file)
{
Resource r = null;
try
{
File asFile = new File (dir, file);
if (asFile.exists())
r = Resource.newResource(asFile);
}
catch (Exception e)
{
r = null;
}
return r;
}
private Resource getFileAsResource (String file)
{
Resource r = null;
try
{
File asFile = new File (file);
if (asFile.exists())
r = Resource.newResource(asFile);
}
catch (Exception e)
{
r = null;
}
return r;
}
private Resource getFileAsResource (File dir, String file)
{
Resource r = null;
try
{
File asFile = new File (dir, file);
if (asFile.exists())
r = Resource.newResource(asFile);
}
catch (Exception e)
{
r = null;
}
return r;
}
}
/* ------------------------------------------------------------ */
public AbstractContextProvider(ServerInstanceWrapper wrapper)
{
_serverWrapper = wrapper;
}
/* ------------------------------------------------------------ */
public ServerInstanceWrapper getServerInstanceWrapper()
{
return _serverWrapper;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.deploy.AppProvider#createContextHandler(org.eclipse.jetty.deploy.App)
*/
public ContextHandler createContextHandler(App app) throws Exception
{
if (app == null)
return null;
if (!(app instanceof OSGiApp))
throw new IllegalStateException(app+" is not a BundleApp");
//Create a ContextHandler suitable to deploy in OSGi
ContextHandler h = ((OSGiApp)app).createContextHandler();
return h;
}
/* ------------------------------------------------------------ */
public void setDeploymentManager(DeploymentManager deploymentManager)
{
_deploymentManager = deploymentManager;
}
/* ------------------------------------------------------------ */
public DeploymentManager getDeploymentManager()
{
return _deploymentManager;
}
}

View File

@ -0,0 +1,114 @@
// ========================================================================
// 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 org.eclipse.jetty.osgi.boot;
import java.util.Dictionary;
import java.util.Hashtable;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
/**
* AbstractBundleApp
*
*
*/
public abstract class AbstractOSGiApp extends App
{
protected Bundle _bundle;
protected Dictionary _properties;
protected ServiceRegistration _registration;
/* ------------------------------------------------------------ */
public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
{
super(manager, provider, originId);
_properties = bundle.getHeaders();
_bundle = bundle;
}
/* ------------------------------------------------------------ */
public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId)
{
super(manager, provider, originId);
_properties = properties;
_bundle = bundle;
}
/* ------------------------------------------------------------ */
public String getBundleSymbolicName()
{
return _bundle.getSymbolicName();
}
/* ------------------------------------------------------------ */
public String getBundleVersionAsString()
{
if (_bundle.getVersion() == null)
return null;
return _bundle.getVersion().toString();
}
/* ------------------------------------------------------------ */
public Bundle getBundle()
{
return _bundle;
}
/* ------------------------------------------------------------ */
public void setRegistration (ServiceRegistration registration)
{
_registration = registration;
}
/* ------------------------------------------------------------ */
public ServiceRegistration getRegistration ()
{
return _registration;
}
/* ------------------------------------------------------------ */
public void registerAsOSGiService() throws Exception
{
if (_registration == null)
{
Dictionary<String,String> properties = new Hashtable<String,String>();
properties.put(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK);
if (getBundleSymbolicName() != null)
properties.put(OSGiWebappConstants.OSGI_WEB_SYMBOLICNAME, getBundleSymbolicName());
if (getBundleVersionAsString() != null)
properties.put(OSGiWebappConstants.OSGI_WEB_VERSION, getBundleVersionAsString());
properties.put(OSGiWebappConstants.OSGI_WEB_CONTEXTPATH, getContextPath());
ServiceRegistration rego = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ContextHandler.class.getName(), getContextHandler(), properties);
setRegistration(rego);
}
}
/* ------------------------------------------------------------ */
protected void deregisterAsOSGiService() throws Exception
{
if (_registration == null)
return;
_registration.unregister();
_registration = null;
}
}

View File

@ -0,0 +1,548 @@
// ========================================================================
// 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 org.eclipse.jetty.osgi.boot;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.internal.webapp.OSGiWebappClassLoader;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.packageadmin.PackageAdmin;
/**
* AbstractWebAppProvider
*
*
*/
public abstract class AbstractWebAppProvider extends AbstractLifeCycle implements AppProvider
{
private static final Logger LOG = Log.getLogger(AbstractWebAppProvider.class);
public static String __defaultConfigurations[] = {
"org.eclipse.jetty.osgi.boot.OSGiWebInfConfiguration",
"org.eclipse.jetty.webapp.WebXmlConfiguration",
"org.eclipse.jetty.osgi.boot.OSGiMetaInfConfiguration",
"org.eclipse.jetty.webapp.FragmentConfiguration",
"org.eclipse.jetty.webapp.JettyWebXmlConfiguration"//,
//"org.eclipse.jetty.osgi.boot.jsp.TagLibOSGiConfiguration"
};
public static void setDefaultConfigurations (String[] defaultConfigs)
{
__defaultConfigurations = defaultConfigs;
}
public static String[] getDefaultConfigurations ()
{
return __defaultConfigurations;
}
private boolean _parentLoaderPriority;
private String _defaultsDescriptor;
private boolean _extractWars = true; //See WebAppContext.extractWars
private String _tldBundles;
private DeploymentManager _deploymentManager;
private String[] _configurationClasses;
private ServerInstanceWrapper _serverWrapper;
/* ------------------------------------------------------------ */
/**
* OSGiApp
*
*
*/
public class OSGiApp extends AbstractOSGiApp
{
private String _contextPath;
private String _webAppPath;
private WebAppContext _webApp;
public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
{
super(manager, provider, bundle, originId);
}
public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId)
{
super(manager, provider, bundle, properties, originId);
}
public void setWebAppContext (WebAppContext webApp)
{
_webApp = webApp;
}
public String getContextPath()
{
return _contextPath;
}
public void setContextPath(String contextPath)
{
this._contextPath = contextPath;
}
public String getBundlePath()
{
return _webAppPath;
}
public void setWebAppPath(String path)
{
this._webAppPath = path;
}
public ContextHandler createContextHandler()
throws Exception
{
if (_webApp != null)
{
configureWebApp();
return _webApp;
}
createWebApp();
return _webApp;
}
protected void createWebApp ()
throws Exception
{
_webApp = newWebApp();
configureWebApp();
}
protected WebAppContext newWebApp ()
{
WebAppContext webApp = new WebAppContext();
webApp.setAttribute(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK);
//make sure we protect also the osgi dirs specified by OSGi Enterprise spec
String[] targets = webApp.getProtectedTargets();
String[] updatedTargets = null;
if (targets != null)
{
updatedTargets = new String[targets.length+OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(targets, 0, updatedTargets, 0, targets.length);
}
else
updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length);
webApp.setProtectedTargets(updatedTargets);
return webApp;
}
public void configureWebApp()
throws Exception
{
//TODO turn this around and let any context.xml file get applied first, and have the properties override
_webApp.setContextPath(_contextPath);
//osgi Enterprise Spec r4 p.427
_webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, _bundle.getBundleContext());
String overrideBundleInstallLocation = (String)_properties.get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
File bundleInstallLocation =
(overrideBundleInstallLocation == null
? BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(_bundle)
: new File(overrideBundleInstallLocation));
URL url = null;
//if the path wasn't set or it was ., then it is the root of the bundle's installed location
if (_webAppPath == null || _webAppPath.length() == 0 || ".".equals(_webAppPath))
{
url = bundleInstallLocation.toURI().toURL();
}
else
{
//Get the location of the root of the webapp inside the installed bundle
if (_webAppPath.startsWith("/") || _webAppPath.startsWith("file:"))
{
url = new File(_webAppPath).toURI().toURL();
}
else if (bundleInstallLocation != null && bundleInstallLocation.isDirectory())
{
url = new File(bundleInstallLocation, _webAppPath).toURI().toURL();
}
else if (bundleInstallLocation != null)
{
Enumeration<URL> urls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(_bundle, _webAppPath);
if (urls != null && urls.hasMoreElements())
url = urls.nextElement();
}
}
if (url == null)
{
throw new IllegalArgumentException("Unable to locate " + _webAppPath
+ " in "
+ (bundleInstallLocation != null ? bundleInstallLocation.getAbsolutePath() : "unlocated bundle '" + _bundle.getSymbolicName()+ "'"));
}
// converts bundleentry: protocol if necessary
_webApp.setWar(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(url).toString());
// Set up what has been configured on the provider
_webApp.setParentLoaderPriority(isParentLoaderPriority());
_webApp.setExtractWAR(isExtract());
if (getConfigurationClasses() != null)
_webApp.setConfigurationClasses(getConfigurationClasses());
else
_webApp.setConfigurationClasses(__defaultConfigurations);
if (getDefaultsDescriptor() != null)
_webApp.setDefaultsDescriptor(getDefaultsDescriptor());
//Set up configuration from manifest headers
//extra classpath
String tmp = (String)_properties.get(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH);
if (tmp != null)
_webApp.setExtraClasspath(tmp);
//web.xml
tmp = (String)_properties.get(OSGiWebappConstants.JETTY_WEB_XML_PATH);
if (tmp != null && tmp.trim().length() != 0)
{
File webXml = getFile (tmp, bundleInstallLocation);
if (webXml != null && webXml.exists())
_webApp.setDescriptor(webXml.getAbsolutePath());
}
//webdefault.xml
tmp = (String)_properties.get(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH);
if (tmp != null)
{
File defaultWebXml = getFile (tmp, bundleInstallLocation);
if (defaultWebXml != null && defaultWebXml.exists())
_webApp.setDefaultsDescriptor(defaultWebXml.getAbsolutePath());
}
//Handle Require-TldBundle
//This is a comma separated list of names of bundles that contain tlds that this webapp uses.
//We add them to the webapp classloader.
String requireTldBundles = (String)_properties.get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
String pathsToTldBundles = getPathsToRequiredBundles(requireTldBundles);
// make sure we provide access to all the jetty bundles by going
// through this bundle.
OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(_serverWrapper.getParentClassLoaderForWebapps(), _webApp, _bundle);
if (pathsToTldBundles != null)
webAppLoader.addClassPath(pathsToTldBundles);
_webApp.setClassLoader(webAppLoader);
// apply any META-INF/context.xml file that is found to configure
// the webapp first
applyMetaInfContextXml();
// pass the value of the require tld bundle so that the TagLibOSGiConfiguration
// can pick it up.
_webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles);
//Set up some attributes
// rfc66
_webApp.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, _bundle.getBundleContext());
// spring-dm-1.2.1 looks for the BundleContext as a different attribute.
// not a spec... but if we want to support
// org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
// then we need to do this to:
_webApp.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), _bundle.getBundleContext());
// also pass the bundle directly. sometimes a bundle does not have a
// bundlecontext.
// it is still useful to have access to the Bundle from the servlet
// context.
_webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, _bundle);
}
protected String getPathsToRequiredBundles (String requireTldBundles)
throws Exception
{
if (requireTldBundles == null) return null;
ServiceReference ref = _bundle.getBundleContext().getServiceReference(org.osgi.service.packageadmin.PackageAdmin.class.getName());
PackageAdmin packageAdmin = (ref == null) ? null : (PackageAdmin)_bundle.getBundleContext().getService(ref);
if (packageAdmin == null)
throw new IllegalStateException("Unable to get PackageAdmin reference to locate required Tld bundles");
StringBuilder paths = new StringBuilder();
String[] symbNames = requireTldBundles.split(", ");
for (String symbName : symbNames)
{
Bundle[] bs = packageAdmin.getBundles(symbName, null);
if (bs == null || bs.length == 0)
{
throw new IllegalArgumentException("Unable to locate the bundle '" + symbName
+ "' specified by "
+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE
+ " in manifest of "
+ (_bundle == null ? "unknown" : _bundle.getSymbolicName()));
}
File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bs[0]);
if (paths.length() > 0) paths.append(", ");
paths.append(f.toURI().toURL().toString());
LOG.debug("getPathsToRequiredBundles: bundle path=" + bs[0].getLocation() + " uri=" + f.toURI());
}
return paths.toString();
}
protected void applyMetaInfContextXml()
throws Exception
{
if (_bundle == null) return;
if (_webApp == null) return;
ClassLoader cl = Thread.currentThread().getContextClassLoader();
LOG.debug("Context classloader = " + cl);
try
{
Thread.currentThread().setContextClassLoader(_webApp.getClassLoader());
//TODO replace this with getting the InputStream so we don't cache in URL
// find if there is a META-INF/context.xml file
URL contextXmlUrl = _bundle.getEntry("/META-INF/jetty-webapp-context.xml");
if (contextXmlUrl == null) return;
// Apply it just as the standard jetty ContextProvider would do
LOG.info("Applying " + contextXmlUrl + " to " + _webApp);
XmlConfiguration xmlConfiguration = new XmlConfiguration(contextXmlUrl);
HashMap properties = new HashMap();
properties.put("Server", getDeploymentManager().getServer());
xmlConfiguration.getProperties().putAll(properties);
xmlConfiguration.configure(_webApp);
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
private File getFile (String file, File bundleInstall)
{
if (file == null)
return null;
if (file.startsWith("/") || file.startsWith("file:/"))
return new File(file);
else
return new File(bundleInstall, file);
}
}
/* ------------------------------------------------------------ */
public AbstractWebAppProvider (ServerInstanceWrapper wrapper)
{
_serverWrapper = wrapper;
}
/* ------------------------------------------------------------ */
/**
* Get the parentLoaderPriority.
*
* @return the parentLoaderPriority
*/
public boolean isParentLoaderPriority()
{
return _parentLoaderPriority;
}
/* ------------------------------------------------------------ */
/**
* Set the parentLoaderPriority.
*
* @param parentLoaderPriority the parentLoaderPriority to set
*/
public void setParentLoaderPriority(boolean parentLoaderPriority)
{
_parentLoaderPriority = parentLoaderPriority;
}
/* ------------------------------------------------------------ */
/**
* Get the defaultsDescriptor.
*
* @return the defaultsDescriptor
*/
public String getDefaultsDescriptor()
{
return _defaultsDescriptor;
}
/* ------------------------------------------------------------ */
/**
* Set the defaultsDescriptor.
*
* @param defaultsDescriptor the defaultsDescriptor to set
*/
public void setDefaultsDescriptor(String defaultsDescriptor)
{
_defaultsDescriptor = defaultsDescriptor;
}
/* ------------------------------------------------------------ */
public boolean isExtract()
{
return _extractWars;
}
/* ------------------------------------------------------------ */
public void setExtract(boolean extract)
{
_extractWars = extract;
}
/* ------------------------------------------------------------ */
/**
* @param tldBundles Comma separated list of bundles that contain tld jars
* that should be setup on the jetty instances created here.
*/
public void setTldBundles(String tldBundles)
{
_tldBundles = tldBundles;
}
/* ------------------------------------------------------------ */
/**
* @return The list of bundles that contain tld jars that should be setup on
* the jetty instances created here.
*/
public String getTldBundles()
{
return _tldBundles;
}
/* ------------------------------------------------------------ */
/**
* @param configurations The configuration class names.
*/
public void setConfigurationClasses(String[] configurations)
{
_configurationClasses = configurations == null ? null : (String[]) configurations.clone();
}
/* ------------------------------------------------------------ */
/**
*
*/
public String[] getConfigurationClasses()
{
return _configurationClasses;
}
/* ------------------------------------------------------------ */
public void setServerInstanceWrapper(ServerInstanceWrapper wrapper)
{
_serverWrapper = wrapper;
}
public ServerInstanceWrapper getServerInstanceWrapper()
{
return _serverWrapper;
}
/* ------------------------------------------------------------ */
/**
* @return
*/
public DeploymentManager getDeploymentManager()
{
return _deploymentManager;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.deploy.AppProvider#setDeploymentManager(org.eclipse.jetty.deploy.DeploymentManager)
*/
public void setDeploymentManager(DeploymentManager deploymentManager)
{
_deploymentManager = deploymentManager;
}
/* ------------------------------------------------------------ */
public ContextHandler createContextHandler(App app) throws Exception
{
if (app == null)
return null;
if (!(app instanceof OSGiApp))
throw new IllegalStateException(app+" is not a BundleApp");
//Create a WebAppContext suitable to deploy in OSGi
ContextHandler ch = ((OSGiApp)app).createContextHandler();
return ch;
}
/* ------------------------------------------------------------ */
public static String getOriginId(Bundle contributor, String path)
{
return contributor.getSymbolicName() + "-" + contributor.getVersion().toString() + (path.startsWith("/") ? path : "/" + path);
}
}

View File

@ -0,0 +1,163 @@
// ========================================================================
// 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 org.eclipse.jetty.osgi.boot;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
/**
* BundleContextProvider
*
* Handles deploying bundles that define a context xml file for configuring them.
*
*
*/
public class BundleContextProvider extends AbstractContextProvider implements BundleProvider
{
private static final Logger LOG = Log.getLogger(AbstractContextProvider.class);
private Map<String, App> _appMap = new HashMap<String, App>();
private Map<Bundle, List<App>> _bundleMap = new HashMap<Bundle, List<App>>();
private ServiceRegistration _serviceRegForBundles;
/* ------------------------------------------------------------ */
public BundleContextProvider(ServerInstanceWrapper wrapper)
{
super(wrapper);
}
/* ------------------------------------------------------------ */
@Override
protected void doStart() throws Exception
{
//register as an osgi service for deploying contexts defined in a bundle, advertising the name of the jetty Server instance we are related to
Dictionary<String,String> properties = new Hashtable<String,String>();
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
_serviceRegForBundles = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(BundleProvider.class.getName(), this, properties);
super.doStart();
}
/* ------------------------------------------------------------ */
@Override
protected void doStop() throws Exception
{
//unregister ourselves
if (_serviceRegForBundles != null)
{
try
{
_serviceRegForBundles.unregister();
}
catch (Exception e)
{
LOG.warn(e);
}
}
}
/* ------------------------------------------------------------ */
/**
* @param bundle
* @param contextFiles
* @return
*/
public boolean bundleAdded (Bundle bundle) throws Exception
{
if (bundle == null)
return false;
String contextFiles = (String)bundle.getHeaders().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
if (contextFiles == null)
contextFiles = (String)bundle.getHeaders().get(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
if (contextFiles == null)
return false;
boolean added = false;
//bundle defines JETTY_CONTEXT_FILE_PATH header,
//a comma separated list of context xml files that each define a ContextHandler
//TODO: (could be WebAppContexts)
String[] tmp = contextFiles.split(",;");
for (String contextFile : tmp)
{
String originId = bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + "-"+contextFile;
OSGiApp app = new OSGiApp(getDeploymentManager(), this, originId, bundle, contextFile);
_appMap.put(originId,app);
List<App> apps = _bundleMap.get(bundle);
if (apps == null)
{
apps = new ArrayList<App>();
_bundleMap.put(bundle, apps);
}
apps.add(app);
getDeploymentManager().addApp(app);
}
return added; //true if even 1 context from this bundle was added
}
/* ------------------------------------------------------------ */
/**
* Bundle has been removed. If it was a context we deployed, undeploy it.
* @param bundle
*
* @return true if this was a context we had deployed, false otherwise
*/
public boolean bundleRemoved (Bundle bundle) throws Exception
{
List<App> apps = _bundleMap.remove(bundle);
boolean removed = false;
if (apps != null)
{
for (App app:apps)
{
_appMap.remove(app.getOriginId());
getDeploymentManager().removeApp(app);
removed = true;
}
}
return removed; //true if even 1 context was removed associated with this bundle
}
}

View File

@ -0,0 +1,22 @@
// ========================================================================
// 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 org.eclipse.jetty.osgi.boot;
import org.osgi.framework.Bundle;
public interface BundleProvider
{
public boolean bundleAdded (Bundle bundle) throws Exception;
public boolean bundleRemoved (Bundle bundle) throws Exception;
}

View File

@ -0,0 +1,232 @@
// ========================================================================
// 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 org.eclipse.jetty.osgi.boot;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
/**
* BundleWebAppProvider
*
* A Jetty Provider that knows how to deploy a WebApp contained inside a Bundle.
*
*/
public class BundleWebAppProvider extends AbstractWebAppProvider implements BundleProvider
{
private static final Logger LOG = Log.getLogger(AbstractWebAppProvider.class);
/**
* Map of Bundle to App. Used when a Bundle contains a webapp.
*/
private Map<Bundle, App> _bundleMap = new HashMap<Bundle, App>();
private ServiceRegistration _serviceRegForBundles;
/* ------------------------------------------------------------ */
/**
* @param wrapper
*/
public BundleWebAppProvider (ServerInstanceWrapper wrapper)
{
super(wrapper);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
*/
protected void doStart() throws Exception
{
//register as an osgi service for deploying bundles, advertising the name of the jetty Server instance we are related to
Dictionary<String,String> properties = new Hashtable<String,String>();
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
_serviceRegForBundles = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(BundleProvider.class.getName(), this, properties);
super.doStart();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
*/
@Override
protected void doStop() throws Exception
{
//unregister ourselves
if (_serviceRegForBundles != null)
{
try
{
_serviceRegForBundles.unregister();
}
catch (Exception e)
{
LOG.warn(e);
}
}
super.doStop();
}
/* ------------------------------------------------------------ */
/**
* A bundle has been added that could be a webapp
* @param bundle
*/
public boolean bundleAdded (Bundle bundle) throws Exception
{
if (bundle == null)
return false;
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
String contextPath = null;
try
{
Dictionary headers = bundle.getHeaders();
//does the bundle have a OSGiWebappConstants.JETTY_WAR_FOLDER_PATH
if (headers.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH) != null)
{
String base = (String)headers.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
contextPath = getContextPath(bundle);
String originId = getOriginId(bundle, base);
//TODO : we don't know whether an app is actually deployed, as deploymentManager swallows all
//exceptions inside the impl of addApp. Need to send the Event and also register as a service
//only if the deployment succeeded
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
app.setWebAppPath(base);
app.setContextPath(contextPath);
_bundleMap.put(bundle, app);
getDeploymentManager().addApp(app);
return true;
}
//does the bundle have a WEB-INF/web.xml
if (bundle.getEntry("/WEB-INF/web.xml") != null)
{
String base = ".";
contextPath = getContextPath(bundle);
String originId = getOriginId(bundle, base);
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
app.setContextPath(contextPath);
app.setWebAppPath(base);
_bundleMap.put(bundle, app);
getDeploymentManager().addApp(app);
return true;
}
//does the bundle define a OSGiWebappConstants.RFC66_WEB_CONTEXTPATH
if (headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) != null)
{
//Could be a static webapp with no web.xml
String base = ".";
contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
String originId = getOriginId(bundle,base);
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
app.setContextPath(contextPath);
app.setWebAppPath(base);
_bundleMap.put(bundle, app);
getDeploymentManager().addApp(app);
return true;
}
return false;
}
catch (Exception e)
{
throw e;
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
/* ------------------------------------------------------------ */
/**
* Bundle has been removed. If it was a webapp we deployed, undeploy it.
* @param bundle
*
* @return true if this was a webapp we had deployed, false otherwise
*/
public boolean bundleRemoved (Bundle bundle) throws Exception
{
App app = _bundleMap.remove(bundle);
if (app != null)
{
getDeploymentManager().removeApp(app);
return true;
}
return false;
}
/* ------------------------------------------------------------ */
private static String getContextPath(Bundle bundle)
{
Dictionary<?, ?> headers = bundle.getHeaders();
String contextPath = (String) headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (contextPath == null)
{
// extract from the last token of the bundle's location:
// (really ?could consider processing the symbolic name as an alternative
// the location will often reflect the version.
// maybe this is relevant when the file is a war)
String location = bundle.getLocation();
String toks[] = location.replace('\\', '/').split("/");
contextPath = toks[toks.length - 1];
// remove .jar, .war etc:
int lastDot = contextPath.lastIndexOf('.');
if (lastDot != -1)
contextPath = contextPath.substring(0, lastDot);
}
if (!contextPath.startsWith("/"))
contextPath = "/" + contextPath;
return contextPath;
}
}

View File

@ -62,8 +62,6 @@ public class JettyBootstrapActivator implements BundleActivator
private ServiceRegistration _registeredServer;
private Server _server;
private JettyContextHandlerServiceTracker _jettyContextHandlerTracker;
private PackageAdminServiceTracker _packageAdminServiceTracker;
@ -91,12 +89,12 @@ public class JettyBootstrapActivator implements BundleActivator
// should activate.
_packageAdminServiceTracker = new PackageAdminServiceTracker(context);
// track Server instances that we should support as deployment targets
// track jetty Server instances that we should support as deployment targets
_jettyServerServiceTracker = new JettyServerServiceTracker();
context.addServiceListener(_jettyServerServiceTracker, "(objectclass=" + Server.class.getName() + ")");
// track ContextHandler class instances and deploy them to one of the known Servers
_jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(_jettyServerServiceTracker);
_jettyContextHandlerTracker = new JettyContextHandlerServiceTracker();
context.addServiceListener(_jettyContextHandlerTracker, "(objectclass=" + ContextHandler.class.getName() + ")");
// Create a default jetty instance right now.
@ -125,7 +123,6 @@ public class JettyBootstrapActivator implements BundleActivator
}
if (_jettyContextHandlerTracker != null)
{
_jettyContextHandlerTracker.stop();
context.removeServiceListener(_jettyContextHandlerTracker);
_jettyContextHandlerTracker = null;
}
@ -159,10 +156,6 @@ public class JettyBootstrapActivator implements BundleActivator
}
finally
{
if (_server != null)
{
_server.stop();
}
INSTANCE = null;
}
}

View File

@ -1,728 +0,0 @@
// ========================================================================
// Copyright (c) 2009-2010 Mortbay, 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:
// Greg Wilkins - initial API and implementation
// ========================================================================
package org.eclipse.jetty.osgi.boot;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.providers.ContextProvider;
import org.eclipse.jetty.deploy.providers.ScanningAppProvider;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
/**
* AppProvider for OSGi. Supports the configuration of ContextHandlers and
* WebApps. Extends the AbstractAppProvider to support the scanning of context
* files located outside of the bundles.
* <p>
* This provider must not be called outside of jetty.boot: it should always be
* called via the OSGi service listener.
* </p>
* <p>
* This provider supports the same set of parameters than the WebAppProvider as
* it supports the deployment of WebAppContexts. Except for the scanning of the
* webapps directory.
* </p>
* <p>
* When the parameter autoInstallOSGiBundles is set to true, OSGi bundles that
* are located in the monitored directory are installed and started after the
* framework as finished auto-starting all the other bundles. Warning: only use
* this for development.
* </p>
*/
public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
{
private static final Logger LOG = Log.getLogger(OSGiAppProvider.class);
private boolean _extractWars = true;
private boolean _parentLoaderPriority = false;
private String _defaultsDescriptor;
private String _tldBundles;
private String[] _configurationClasses;
private boolean _autoInstallOSGiBundles = true;
// Keep track of the bundles that were installed and that are waiting for
// the
// framework to complete its initialization.
Set<Bundle> _pendingBundlesToStart = null;
/**
* When a context file corresponds to a deployed bundle and is changed we
* reload the corresponding bundle.
*/
private static class Filter implements FilenameFilter
{
OSGiAppProvider _enclosedInstance;
public boolean accept(File dir, String name)
{
File file = new File(dir, name);
if (fileMightBeAnOSGiBundle(file)) { return true; }
if (!file.isDirectory())
{
String contextName = getDeployedAppName(name);
if (contextName != null)
{
App app = _enclosedInstance.getDeployedApps().get(contextName);
return app != null;
}
}
return false;
}
}
/**
* @param contextFileName for example myContext.xml
* @return The context, for example: myContext; null if this was not a
* suitable contextFileName.
*/
private static String getDeployedAppName(String contextFileName)
{
String lowername = contextFileName.toLowerCase();
if (lowername.endsWith(".xml"))
{
String contextName = contextFileName.substring(0, lowername.length() - ".xml".length());
return contextName;
}
return null;
}
/**
* Reading the display name of a webapp is really not sufficient for
* indexing the various deployed ContextHandlers.
*
* @param context
* @return
*/
private String getContextHandlerAppName(ContextHandler context)
{
String appName = context.getDisplayName();
if (appName == null || appName.length() == 0 || getDeployedApps().containsKey(appName))
{
if (context instanceof WebAppContext)
{
appName = ((WebAppContext) context).getContextPath();
if (getDeployedApps().containsKey(appName))
{
appName = "noDisplayName" + context.getClass().getSimpleName() + context.hashCode();
}
}
else
{
appName = "noDisplayName" + context.getClass().getSimpleName() + context.hashCode();
}
}
return appName;
}
/**
* Default OSGiAppProvider constructed when none are defined in the
* jetty.xml configuration.
*/
public OSGiAppProvider()
{
super(new Filter());
((Filter) super._filenameFilter)._enclosedInstance = this;
}
/**
* Default OSGiAppProvider constructed when none are defined in the
* jetty.xml configuration.
*
* @param contextsDir
*/
public OSGiAppProvider(File contextsDir) throws IOException
{
this();
setMonitoredDirResource(Resource.newResource(contextsDir.toURI()));
}
/**
* Returns the ContextHandler that was created by WebappRegistractionHelper
*
* @see AppProvider
*/
public ContextHandler createContextHandler(App app) throws Exception
{
// return pre-created Context
ContextHandler wah = app.getContextHandler();
if (wah == null)
{
// for some reason it was not defined when the App was constructed.
// we don't support this situation at this point.
// once the WebAppRegistrationHelper is refactored, the code
// that creates the ContextHandler will actually be here.
throw new IllegalStateException("The App must be passed the " + "instance of the ContextHandler when it is constructed");
}
if (_configurationClasses != null && wah instanceof WebAppContext)
{
((WebAppContext) wah).setConfigurationClasses(_configurationClasses);
}
if (_defaultsDescriptor != null)
((WebAppContext) wah).setDefaultsDescriptor(_defaultsDescriptor);
return app.getContextHandler();
}
/**
* @see AppProvider
*/
@Override
public void setDeploymentManager(DeploymentManager deploymentManager)
{
super.setDeploymentManager(deploymentManager);
}
private static String getOriginId(Bundle contributor, String pathInBundle)
{
return contributor.getSymbolicName() + "-" + contributor.getVersion().toString() + (pathInBundle.startsWith("/") ? pathInBundle : "/" + pathInBundle);
}
/**
* @param context
* @throws Exception
*/
public void addContext(Bundle contributor, String pathInBundle, ContextHandler context) throws Exception
{
addContext(getOriginId(contributor, pathInBundle), context);
}
/**
* @param context
* @throws Exception
*/
public void addContext(String originId, ContextHandler context) throws Exception
{
// TODO apply configuration specific to this provider
if (context instanceof WebAppContext)
{
((WebAppContext) context).setExtractWAR(isExtract());
}
// wrap context as an App
App app = new App(getDeploymentManager(), this, originId, context);
String appName = getContextHandlerAppName(context);
getDeployedApps().put(appName, app);
getDeploymentManager().addApp(app);
}
/**
* Called by the scanner of the context files directory. If we find the
* corresponding deployed App we reload it by returning the App. Otherwise
* we return null and nothing happens: presumably the corresponding OSGi
* webapp is not ready yet.
*
* @return the corresponding already deployed App so that it will be
* reloaded. Otherwise returns null.
*/
@Override
protected App createApp(String filename)
{
// find the corresponding bundle and ContextHandler or WebAppContext
// and reload the corresponding App.
// see the 2 pass of the refactoring of the WebAppRegistrationHelper.
String name = getDeployedAppName(filename);
if (name != null) { return getDeployedApps().get(name); }
return null;
}
public void removeContext(ContextHandler context) throws Exception
{
String appName = getContextHandlerAppName(context);
App app = getDeployedApps().remove(context.getDisplayName());
if (app == null)
{
// try harder to undeploy this context handler.
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=330098
appName = null;
for (Entry<String, App> deployedApp : getDeployedApps().entrySet())
{
if (deployedApp.getValue().getContextHandler() == context)
{
app = deployedApp.getValue();
appName = deployedApp.getKey();
break;
}
}
if (appName != null)
{
getDeployedApps().remove(appName);
}
}
if (app != null)
{
getDeploymentManager().removeApp(app);
}
}
/* ------------------------------------------------------------ */
/**
* Get the parentLoaderPriority.
*
* @return the parentLoaderPriority
*/
public boolean isParentLoaderPriority()
{
return _parentLoaderPriority;
}
/* ------------------------------------------------------------ */
/**
* Set the parentLoaderPriority.
*
* @param parentLoaderPriority the parentLoaderPriority to set
*/
public void setParentLoaderPriority(boolean parentLoaderPriority)
{
_parentLoaderPriority = parentLoaderPriority;
}
/* ------------------------------------------------------------ */
/**
* Get the defaultsDescriptor.
*
* @return the defaultsDescriptor
*/
public String getDefaultsDescriptor()
{
return _defaultsDescriptor;
}
/* ------------------------------------------------------------ */
/**
* Set the defaultsDescriptor.
*
* @param defaultsDescriptor the defaultsDescriptor to set
*/
public void setDefaultsDescriptor(String defaultsDescriptor)
{
_defaultsDescriptor = defaultsDescriptor;
}
/**
* The context xml directory. In fact it is the directory watched by the
* scanner.
*/
public File getContextXmlDirAsFile()
{
try
{
Resource monitoredDir = getMonitoredDirResource();
if (monitoredDir == null) return null;
return monitoredDir.getFile();
}
catch (IOException e)
{
LOG.warn(e);
return null;
}
}
/* ------------------------------------------------------------ */
/**
* The context xml directory. In fact it is the directory watched by the
* scanner.
*/
public String getContextXmlDir()
{
try
{
Resource monitoredDir = getMonitoredDirResource();
if (monitoredDir == null) return null;
return monitoredDir.getFile().toURI().toString();
}
catch (IOException e)
{
LOG.warn(e);
return null;
}
}
public boolean isExtract()
{
return _extractWars;
}
public void setExtract(boolean extract)
{
_extractWars = extract;
}
/**
* @return true when this app provider locates osgi bundles and features in
* its monitored directory and installs them. By default true if
* there is a folder to monitor.
*/
public boolean isAutoInstallOSGiBundles()
{
return _autoInstallOSGiBundles;
}
/**
* &lt;autoInstallOSGiBundles&gt;true&lt;/autoInstallOSGiBundles&gt;
*
* @param installingOSGiBundles
*/
public void setAutoInstallOSGiBundles(boolean installingOSGiBundles)
{
_autoInstallOSGiBundles = installingOSGiBundles;
}
/* ------------------------------------------------------------ */
/**
* Set the directory in which to look for context XML files.
* <p>
* If a webapp call "foo/" or "foo.war" is discovered in the monitored
* directory, then the ContextXmlDir is examined to see if a foo.xml file
* exists. If it does, then this deployer will not deploy the webapp and the
* ContextProvider should be used to act on the foo.xml file.
* </p>
* <p>
* Also if this directory contains some osgi bundles, it will install them.
* </p>
*
* @see ContextProvider
* @param contextsDir
*/
public void setContextXmlDir(String contextsDir)
{
setMonitoredDirName(contextsDir);
}
/**
* @param tldBundles Comma separated list of bundles that contain tld jars
* that should be setup on the jetty instances created here.
*/
public void setTldBundles(String tldBundles)
{
_tldBundles = tldBundles;
}
/**
* @return The list of bundles that contain tld jars that should be setup on
* the jetty instances created here.
*/
public String getTldBundles()
{
return _tldBundles;
}
/**
* @param configurations The configuration class names.
*/
public void setConfigurationClasses(String[] configurations)
{
_configurationClasses = configurations == null ? null : (String[]) configurations.clone();
}
/* ------------------------------------------------------------ */
/**
*
*/
public String[] getConfigurationClasses()
{
return _configurationClasses;
}
/**
* Overridden to install the OSGi bundles found in the monitored folder.
*/
@Override
protected void doStart() throws Exception
{
if (isAutoInstallOSGiBundles())
{
if (getMonitoredDirResource() == null)
{
setAutoInstallOSGiBundles(false);
LOG.info("Disable autoInstallOSGiBundles as there is not contexts folder to monitor.");
}
else
{
File scandir = null;
try
{
scandir = getMonitoredDirResource().getFile();
if (!scandir.exists() || !scandir.isDirectory())
{
setAutoInstallOSGiBundles(false);
LOG.warn("Disable autoInstallOSGiBundles as the contexts folder '" + scandir.getAbsolutePath() + " does not exist.");
scandir = null;
}
}
catch (IOException ioe)
{
setAutoInstallOSGiBundles(false);
LOG.warn("Disable autoInstallOSGiBundles as the contexts folder '" + getMonitoredDirResource().getURI() + " does not exist.");
scandir = null;
}
if (scandir != null)
{
for (File file : scandir.listFiles())
{
if (fileMightBeAnOSGiBundle(file))
{
installBundle(file, false);
}
}
}
}
}
super.doStart();
if (isAutoInstallOSGiBundles())
{
Scanner.ScanCycleListener scanCycleListner = new AutoStartWhenFrameworkHasCompleted(this);
super.addScannerListener(scanCycleListner);
}
}
/**
* When the file is a jar or a folder, we look if it looks like an OSGi
* bundle. In that case we install it and start it.
* <p>
* Really a simple trick to get going quickly with development.
* </p>
*/
@Override
protected void fileAdded(String filename) throws Exception
{
File file = new File(filename);
if (isAutoInstallOSGiBundles() && file.exists() && fileMightBeAnOSGiBundle(file))
{
installBundle(file, true);
}
else
{
super.fileAdded(filename);
}
}
/**
* @param file
* @return
*/
private static boolean fileMightBeAnOSGiBundle(File file)
{
if (file.isDirectory())
{
if (new File(file, "META-INF/MANIFEST.MF").exists()) { return true; }
}
else if (file.getName().endsWith(".jar")) { return true; }
return false;
}
@Override
protected void fileChanged(String filename) throws Exception
{
File file = new File(filename);
if (isAutoInstallOSGiBundles() && fileMightBeAnOSGiBundle(file))
{
updateBundle(file);
}
else
{
super.fileChanged(filename);
}
}
@Override
protected void fileRemoved(String filename) throws Exception
{
File file = new File(filename);
if (isAutoInstallOSGiBundles() && fileMightBeAnOSGiBundle(file))
{
uninstallBundle(file);
}
else
{
super.fileRemoved(filename);
}
}
/**
* Returns a bundle according to its location. In the version 1.6 of
* org.osgi.framework, BundleContext.getBundle(String) is what we want.
* However to support older versions of OSGi. We use our own local reference
* mechanism.
*
* @param location
* @return
*/
protected Bundle getBundle(BundleContext bc, String location)
{
// not available in older versions of OSGi:
// return bc.getBundle(location);
for (Bundle b : bc.getBundles())
{
if (b.getLocation().equals(location)) { return b; }
}
return null;
}
protected synchronized Bundle installBundle(File file, boolean start)
{
try
{
BundleContext bc = JettyBootstrapActivator.getBundleContext();
String location = file.toURI().toString();
Bundle b = getBundle(bc, location);
if (b == null)
{
b = bc.installBundle(location);
}
if (b == null)
{
// not sure we will ever be here,
// most likely a BundleException was thrown
LOG.warn("The file " + location + " is not an OSGi bundle.");
return null;
}
if (start && b.getHeaders().get(Constants.FRAGMENT_HOST) == null)
{
// not a fragment, try to start it. if the framework has finished
// auto-starting.
if (!PackageAdminServiceTracker.INSTANCE.frameworkHasCompletedAutostarts())
{
if (_pendingBundlesToStart == null)
{
_pendingBundlesToStart = new HashSet<Bundle>();
}
_pendingBundlesToStart.add(b);
return null;
}
else
{
b.start();
}
}
return b;
}
catch (BundleException e)
{
LOG.warn("Unable to " + (start ? "start" : "install") + " the bundle " + file.getAbsolutePath(), e);
}
return null;
}
protected void uninstallBundle(File file)
{
try
{
Bundle b = getBundle(JettyBootstrapActivator.getBundleContext(), file.toURI().toString());
b.stop();
b.uninstall();
}
catch (BundleException e)
{
LOG.warn("Unable to uninstall the bundle " + file.getAbsolutePath(), e);
}
}
protected void updateBundle(File file)
{
try
{
Bundle b = getBundle(JettyBootstrapActivator.getBundleContext(), file.toURI().toString());
if (b == null)
{
installBundle(file, true);
}
else if (b.getState() == Bundle.ACTIVE)
{
b.update();
}
else
{
b.start();
}
}
catch (BundleException e)
{
LOG.warn("Unable to update the bundle " + file.getAbsolutePath(), e);
}
}
}
/**
* At the end of each scan, if there are some bundles to be started, look if the
* framework has completed its autostart. In that case start those bundles.
*/
class AutoStartWhenFrameworkHasCompleted implements Scanner.ScanCycleListener
{
private static final Logger LOG = Log.getLogger(AutoStartWhenFrameworkHasCompleted.class);
private final OSGiAppProvider _appProvider;
AutoStartWhenFrameworkHasCompleted(OSGiAppProvider appProvider)
{
_appProvider = appProvider;
}
public void scanStarted(int cycle) throws Exception
{
}
public void scanEnded(int cycle) throws Exception
{
if (_appProvider._pendingBundlesToStart != null && PackageAdminServiceTracker.INSTANCE.frameworkHasCompletedAutostarts())
{
Iterator<Bundle> it = _appProvider._pendingBundlesToStart.iterator();
while (it.hasNext())
{
Bundle b = it.next();
if (b.getHeaders().get(Constants.FRAGMENT_HOST) != null)
{
continue;
}
try
{
b.start();
}
catch (BundleException e)
{
LOG.warn("Unable to start the bundle " + b.getLocation(), e);
}
}
_appProvider._pendingBundlesToStart = null;
}
}
}

View File

@ -0,0 +1,55 @@
// ========================================================================
// 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.apache.org/licenses/LICENSE-2.0.txt
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.osgi.boot;
import java.util.Dictionary;
import java.util.Hashtable;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.bindings.StandardDeployer;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
/**
* OSGiDeployer
*
*
*/
public class OSGiDeployer extends StandardDeployer
{
/* ------------------------------------------------------------ */
public void processBinding(Node node, App app) throws Exception
{
//TODO how to NOT send this event if its not a webapp!
EventSender.getInstance().send(EventSender.DEPLOYING_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
try
{
super.processBinding(node,app);
((AbstractOSGiApp)app).registerAsOSGiService();
EventSender.getInstance().send(EventSender.DEPLOYED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
}
catch (Exception e)
{
EventSender.getInstance().send(EventSender.FAILED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
throw e;
}
}
}

View File

@ -0,0 +1,110 @@
// ========================================================================
// 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 org.eclipse.jetty.osgi.boot;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.MetaInfConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
public class OSGiMetaInfConfiguration extends MetaInfConfiguration
{
private static final Logger LOG = Log.getLogger(OSGiMetaInfConfiguration.class);
/**
* Inspect bundle fragments associated with the bundle of the webapp for web-fragment, resources, tlds.
*
* @see org.eclipse.jetty.webapp.MetaInfConfiguration#preConfigure(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
public void preConfigure(final WebAppContext context) throws Exception
{
List<Resource> frags = (List<Resource>) context.getAttribute(METAINF_FRAGMENTS);
List<Resource> resfrags = (List<Resource>) context.getAttribute(METAINF_RESOURCES);
List<Resource> tldfrags = (List<Resource>) context.getAttribute(METAINF_TLDS);
Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles((Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE));
//TODO not convinced we need to do this, as we added any fragment jars to the MetaData.webInfJars in OSGiWebInfConfiguration,
//so surely the web-fragments and resources tlds etc can be discovered normally?
for (Bundle frag : fragments)
{
URL webFrag = frag.getEntry("/META-INF/web-fragment.xml");
Enumeration<URL> resEnum = frag.findEntries("/META-INF/resources", "*", true);
Enumeration<URL> tldEnum = frag.findEntries("/META-INF", "*.tld", false);
if (webFrag != null || (resEnum != null && resEnum.hasMoreElements()) || (tldEnum != null && tldEnum.hasMoreElements()))
{
try
{
if (webFrag != null)
{
if (frags == null)
{
frags = new ArrayList<Resource>();
context.setAttribute(METAINF_FRAGMENTS, frags);
}
frags.add(Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(frag).toURI()));
}
if (resEnum != null && resEnum.hasMoreElements())
{
URL resourcesEntry = frag.getEntry("/META-INF/resources/");
if (resourcesEntry == null)
{
// probably we found some fragments to a
// bundle.
// those are already contributed.
// so we skip this.
}
else
{
if (resfrags == null)
{
resfrags = new ArrayList<Resource>();
context.setAttribute(METAINF_RESOURCES, resfrags);
}
resfrags.add(Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(resourcesEntry)));
}
}
if (tldEnum != null && tldEnum.hasMoreElements())
{
if (tldfrags == null)
{
tldfrags = new ArrayList<Resource>();
context.setAttribute(METAINF_TLDS, tldfrags);
}
while (tldEnum.hasMoreElements())
{
URL tldUrl = tldEnum.nextElement();
tldfrags.add(Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(tldUrl)));
}
}
}
catch (Exception e)
{
LOG.warn("Unable to locate the bundle " + frag.getBundleId(), e);
}
}
}
super.preConfigure(context);
}
}

View File

@ -19,6 +19,38 @@ package org.eclipse.jetty.osgi.boot;
*/
public class OSGiServerConstants
{
/**
* Usual system property used as the hostname for a typical jetty
* configuration.
*/
public static final String JETTY_HOME = "jetty.home";
/**
* System property to point to a bundle that embeds a jetty configuration
* and that jetty configuration should be the default jetty server. First we
* look for jetty.home. If we don't find it then we look for this property.
*/
public static final String JETTY_HOME_BUNDLE = "jetty.home.bundle";
/**
* Usual system property used as the hostname for a typical jetty
* configuration.
*/
public static final String JETTY_HOST = "jetty.host";
/**
* Usual system property used as the port for http for a typical jetty
* configuration.
*/
public static final String JETTY_PORT = "jetty.port";
/**
* Usual system property used as the port for https for a typical jetty
* configuration.
*/
public static final String JETTY_PORT_SSL = "jetty.port.ssl";
//for managed jetty instances, name of the configuration parameters
/**
* PID of the jetty servers's ManagedFactory

View File

@ -0,0 +1,41 @@
// ========================================================================
// 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.apache.org/licenses/LICENSE-2.0.txt
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.osgi.boot;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.bindings.StandardUndeployer;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
/**
* OSGiUndeployer
*
*
*/
public class OSGiUndeployer extends StandardUndeployer
{
/* ------------------------------------------------------------ */
public void processBinding(Node node, App app) throws Exception
{
EventSender.getInstance().send(EventSender.UNDEPLOYING_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
super.processBinding(node,app);
EventSender.getInstance().send(EventSender.UNDEPLOYED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
((AbstractOSGiApp)app).deregisterAsOSGiService();
}
}

View File

@ -0,0 +1,267 @@
// ========================================================================
// 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 org.eclipse.jetty.osgi.boot;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.regex.Pattern;
import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
/**
* OSGiWebInfConfiguration
*
* Handle adding resources found in bundle fragments, and add them into the
*/
public class OSGiWebInfConfiguration extends WebInfConfiguration
{
private static final Logger LOG = Log.getLogger(WebInfConfiguration.class);
public static final String CONTAINER_BUNDLE_PATTERN = "org.eclipse.jetty.server.webapp.containerIncludeBundlePattern";
/**
* Check to see if there have been any bundle symbolic names added of bundles that should be
* regarded as being on the container classpath, and scanned for fragments, tlds etc etc.
* This can be defined in:
* <ol>
* <li>SystemProperty SYS_PROP_TLD_BUNDLES</li>
* <li>DeployerManager.setContextAttribute CONTAINER_BUNDLE_PATTERN</li>
* </ol>
*
* We also allow individual bundles to specify particular bundles that might include TLDs via the Require-Tlds
* MANIFEST.MF header. This is processed in the TagLibOSGiConfiguration class.
*
* @see org.eclipse.jetty.webapp.WebInfConfiguration#preConfigure(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
public void preConfigure(final WebAppContext context) throws Exception
{
super.preConfigure(context);
//Check to see if there have been any bundle symbolic names added of bundles that should be
//regarded as being on the container classpath, and scanned for fragments, tlds etc etc.
//This can be defined in:
// 1. SystemProperty SYS_PROP_TLD_BUNDLES
// 2. DeployerManager.setContextAttribute CONTAINER_BUNDLE_PATTERN
String tmp = (String)context.getAttribute(CONTAINER_BUNDLE_PATTERN);
Pattern pattern = (tmp==null?null:Pattern.compile(tmp));
List<String> names = new ArrayList<String>();
tmp = System.getProperty("org.eclipse.jetty.osgi.tldbundles");
if (tmp != null)
{
StringTokenizer tokenizer = new StringTokenizer(tmp, ", \n\r\t", false);
while (tokenizer.hasMoreTokens())
names.add(tokenizer.nextToken());
}
HashSet<Resource> matchingResources = new HashSet<Resource>();
if ( !names.isEmpty() || pattern != null)
{
Bundle[] bundles = FrameworkUtil.getBundle(OSGiWebInfConfiguration.class).getBundleContext().getBundles();
for (Bundle bundle : bundles)
{
if (pattern != null)
{
// if bundle symbolic name matches the pattern
if (pattern.matcher(bundle.getSymbolicName()).matches())
{
//get the file location of the jar and put it into the list of container jars that will be scanned for stuff (including tlds)
matchingResources.addAll(getBundleAsResource(bundle));
}
}
if (names != null)
{
//if there is an explicit bundle name, then check if it matches
if (names.contains(bundle.getSymbolicName()))
matchingResources.addAll(getBundleAsResource(bundle));
}
}
}
for (Resource r:matchingResources)
{
context.getMetaData().addContainerJar(r);
}
}
/**
* Consider the fragment bundles associated with the bundle of the webapp being deployed.
*
*
* @see org.eclipse.jetty.webapp.WebInfConfiguration#findJars(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
protected List<Resource> findJars (WebAppContext context)
throws Exception
{
List<Resource> mergedResources = new ArrayList<Resource>();
//get jars from WEB-INF/lib if there are any
List<Resource> webInfJars = super.findJars(context);
if (webInfJars != null)
mergedResources.addAll(webInfJars);
//add fragment jars as if in WEB-INF/lib of the associated webapp
Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles((Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE));
for (Bundle frag : fragments)
{
File fragFile = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(frag);
mergedResources.add(Resource.newResource(fragFile.toURI()));
}
return mergedResources;
}
/**
* Allow fragments to supply some resources that are added to the baseResource of the webapp.
*
* The resources can be either prepended or appended to the baseResource.
*
* @see org.eclipse.jetty.webapp.WebInfConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
public void configure(WebAppContext context) throws Exception
{
TreeMap<String, Resource> patchResourcesPath = new TreeMap<String, Resource>();
TreeMap<String, Resource> appendedResourcesPath = new TreeMap<String, Resource>();
Bundle bundle = (Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
if (bundle != null)
{
//TODO anything we need to do to improve PackageAdminServiceTracker?
Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(bundle);
if (fragments != null && fragments.length != 0)
{
// sorted extra resource base found in the fragments.
// the resources are either overriding the resourcebase found in the
// web-bundle
// or appended.
// amongst each resource we sort them according to the alphabetical
// order
// of the name of the internal folder and the symbolic name of the
// fragment.
// this is useful to make sure that the lookup path of those
// resource base defined by fragments is always the same.
// This natural order could be abused to define the order in which
// the base resources are
// looked up.
for (Bundle frag : fragments)
{
String fragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH);
String patchFragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH);
if (fragFolder != null)
{
URL fragUrl = frag.getEntry(fragFolder);
if (fragUrl == null) { throw new IllegalArgumentException("Unable to locate " + fragFolder
+ " inside "
+ " the fragment '"
+ frag.getSymbolicName()
+ "'"); }
fragUrl = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(fragUrl);
String key = fragFolder.startsWith("/") ? fragFolder.substring(1) : fragFolder;
appendedResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(fragUrl));
}
if (patchFragFolder != null)
{
URL patchFragUrl = frag.getEntry(patchFragFolder);
if (patchFragUrl == null)
{
throw new IllegalArgumentException("Unable to locate " + patchFragUrl
+ " inside fragment '"+frag.getSymbolicName()+ "'");
}
patchFragUrl = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(patchFragUrl);
String key = patchFragFolder.startsWith("/") ? patchFragFolder.substring(1) : patchFragFolder;
patchResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(patchFragUrl));
}
}
if (!appendedResourcesPath.isEmpty())
context.setAttribute(WebInfConfiguration.RESOURCE_URLS, new ArrayList<Resource>(appendedResourcesPath.values()));
}
}
super.configure(context);
// place the patch resources at the beginning of the contexts's resource base
if (!patchResourcesPath.isEmpty())
{
Resource[] resources = new Resource[1+patchResourcesPath.size()];
ResourceCollection mergedResources = new ResourceCollection (patchResourcesPath.values().toArray(new Resource[patchResourcesPath.size()]));
System.arraycopy(patchResourcesPath.values().toArray(new Resource[patchResourcesPath.size()]), 0, resources, 0, patchResourcesPath.size());
resources[resources.length-1] = context.getBaseResource();
context.setBaseResource(new ResourceCollection(resources));
}
}
/**
* Resolves the bundle. Usually that would be a single URL per bundle. But we do some more work if there are jars
* embedded in the bundle.
*/
private List<Resource> getBundleAsResource(Bundle bundle)
throws Exception
{
List<Resource> resources = new ArrayList<Resource>();
File file = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle);
if (file.isDirectory())
{
for (File f : file.listFiles())
{
if (f.getName().endsWith(".jar") && f.isFile())
{
resources.add(Resource.newResource(f));
}
else if (f.isDirectory() && f.getName().equals("lib"))
{
for (File f2 : file.listFiles())
{
if (f2.getName().endsWith(".jar") && f2.isFile())
{
resources.add(Resource.newResource(f));
}
}
}
}
resources.add(Resource.newResource(file)); //TODO really???
}
else
{
resources.add(Resource.newResource(file));
}
return resources;
}
}

View File

@ -19,6 +19,20 @@ package org.eclipse.jetty.osgi.boot;
*/
public class OSGiWebappConstants
{
/** service property osgi.web.symbolicname. See OSGi r4 */
public static final String OSGI_WEB_SYMBOLICNAME = "osgi.web.symbolicname";
/** service property osgi.web.symbolicname. See OSGi r4 */
public static final String OSGI_WEB_VERSION = "osgi.web.version";
/** service property osgi.web.contextpath. See OSGi r4 */
public static final String OSGI_WEB_CONTEXTPATH = "osgi.web.contextpath";
/** See OSGi r4 p.427 */
public static final String OSGI_BUNDLECONTEXT = "osgi-bundlecontext";
/** url scheme to deploy war file as bundled webapp */
public static final String RFC66_WAR_URL_SCHEME = "war";
@ -55,32 +69,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.
*/
@ -90,4 +132,14 @@ public class OSGiWebappConstants
* Both the name of the manifest header and the name of the service property.
*/
public static final String SERVICE_PROP_REQUIRE_TLD_BUNDLE = REQUIRE_TLD_BUNDLE;
public static final String WATERMARK = "o.e.j.o.b.watermark";
/**
* Set of extra dirs that must not be served by osgi webapps
*/
public static final String[] DEFAULT_PROTECTED_OSGI_TARGETS = {"/osgi-inf", "/osgi-opts"};
}

View File

@ -0,0 +1,168 @@
package org.eclipse.jetty.osgi.boot;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
/**
* ServiceContextProvider
*
*
*/
public class ServiceContextProvider extends AbstractContextProvider implements ServiceProvider
{
private static final Logger LOG = Log.getLogger(AbstractContextProvider.class);
private Map<ServiceReference, App> _serviceMap = new HashMap<ServiceReference, App>();
private ServiceRegistration _serviceRegForServices;
/**
* ServiceApp
*
*
*/
public class ServiceApp extends OSGiApp
{
public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String contextFile, String originId)
{
super(manager, provider, bundle, properties, contextFile, originId);
}
public ServiceApp(DeploymentManager manager, AppProvider provider, String originId, Bundle bundle, String contextFile)
{
super(manager, provider, originId, bundle, contextFile);
}
@Override
public void registerAsOSGiService() throws Exception
{
//not applicable for apps that are already services
}
@Override
protected void deregisterAsOSGiService() throws Exception
{
//not applicable for apps that are already services
}
}
/* ------------------------------------------------------------ */
public ServiceContextProvider(ServerInstanceWrapper wrapper)
{
super(wrapper);
}
/* ------------------------------------------------------------ */
public boolean serviceAdded (ServiceReference serviceRef, ContextHandler context)
{
if (context == null || serviceRef == null)
return false;
String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return false; //this service represents a contexthandler that has already been registered as a service by another of our deployers
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
try
{
//See if there is a context file to apply to this pre-made context
String contextFile = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
if (contextFile == null)
contextFile = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
String[] keys = serviceRef.getPropertyKeys();
Dictionary properties = new Hashtable<String, Object>();
if (keys != null)
{
for (String key:keys)
properties.put(key, serviceRef.getProperty(key));
}
Bundle bundle = serviceRef.getBundle();
String originId = bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + "-"+contextFile;
ServiceApp app = new ServiceApp(getDeploymentManager(), this, bundle, properties, contextFile, originId);
app.setHandler(context); //set the pre=made ContextHandler instance
_serviceMap.put(serviceRef, app);
getDeploymentManager().addApp(app);
return true;
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
/* ------------------------------------------------------------ */
public boolean serviceRemoved (ServiceReference serviceRef, ContextHandler context)
{
if (context == null || serviceRef == null)
return false;
String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return false; //this service represents a contexthandler that will be deregistered as a service by another of our deployers
App app = _serviceMap.remove(serviceRef);
if (app != null)
{
getDeploymentManager().removeApp(app);
return true;
}
return false;
}
/* ------------------------------------------------------------ */
@Override
protected void doStart() throws Exception
{
//register as an osgi service for deploying contexts defined in a bundle, advertising the name of the jetty Server instance we are related to
Dictionary<String,String> properties = new Hashtable<String,String>();
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
//register as an osgi service for deploying contexts, advertising the name of the jetty Server instance we are related to
_serviceRegForServices = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ServiceProvider.class.getName(), this, properties);
super.doStart();
}
/* ------------------------------------------------------------ */
@Override
protected void doStop() throws Exception
{
//unregister ourselves
if (_serviceRegForServices != null)
{
try
{
_serviceRegForServices.unregister();
}
catch (Exception e)
{
LOG.warn(e);
}
}
super.doStop();
}
}

View File

@ -0,0 +1,23 @@
// ========================================================================
// 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 org.eclipse.jetty.osgi.boot;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.osgi.framework.ServiceReference;
public interface ServiceProvider
{
public boolean serviceAdded (ServiceReference ref, ContextHandler handler) throws Exception;
public boolean serviceRemoved (ServiceReference ref, ContextHandler handler) throws Exception;
}

View File

@ -0,0 +1,230 @@
package org.eclipse.jetty.osgi.boot;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.osgi.boot.utils.EventSender;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
/**
* ServiceWebAppProvider
*
* Jetty Provider that knows how to deploy a WebApp that has been registered as an OSGi service.
*
*/
public class ServiceWebAppProvider extends AbstractWebAppProvider implements ServiceProvider
{
private static final Logger LOG = Log.getLogger(AbstractWebAppProvider.class);
/**
* Map of ServiceRef to App. Used when it is an osgi service that is a WebAppContext.
*/
private Map<ServiceReference, App> _serviceMap = new HashMap<ServiceReference, App>();
private ServiceRegistration _serviceRegForServices;
/**
* ServiceApp
*
*
*/
public class ServiceApp extends OSGiApp
{
public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId)
{
super(manager, provider, bundle, properties, originId);
}
public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
{
super(manager, provider, bundle, originId);
}
@Override
public void registerAsOSGiService() throws Exception
{
//not applicable for apps that are already services
}
@Override
protected void deregisterAsOSGiService() throws Exception
{
//not applicable for apps that are already services
}
}
/* ------------------------------------------------------------ */
/**
* @param wrapper
*/
public ServiceWebAppProvider (ServerInstanceWrapper wrapper)
{
super(wrapper);
}
/* ------------------------------------------------------------ */
/**
* A webapp that was deployed as an osgi service has been added,
* and we want to deploy it.
*
* @param context the webapp
*/
public boolean serviceAdded (ServiceReference serviceRef, ContextHandler context)
{
if (context == null || !(context instanceof WebAppContext))
return false;
String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return false; //this service represents a webapp that has already been registered as a service by another of our deployers
WebAppContext webApp = (WebAppContext)context;
Dictionary properties = new Hashtable<String,String>();
String contextPath = (String)serviceRef.getProperty(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (contextPath == null)
contextPath = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH);
if (contextPath == null)
return false; //No context path
String base = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
if (base == null)
base = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_WAR);
if (base == null)
return false; //No webapp base
String webdefaultXml = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH);
if (webdefaultXml == null)
webdefaultXml = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_DEFAULT_WEB_XML_PATH);
if (webdefaultXml != null)
properties.put(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH, webdefaultXml);
String webXml = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH);
if (webXml == null)
webXml = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_WEB_XML_PATH);
if (webXml != null)
properties.put(OSGiWebappConstants.JETTY_WEB_XML_PATH, webXml);
String extraClassPath = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH);
if (extraClassPath == null)
extraClassPath = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH);
if (extraClassPath != null)
properties.put(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH, extraClassPath);
String bundleInstallOverride = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
if (bundleInstallOverride == null)
bundleInstallOverride = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE);
if (bundleInstallOverride != null)
properties.put(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE, bundleInstallOverride);
String requiredTlds = (String)serviceRef.getProperty(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
if (requiredTlds == null)
requiredTlds = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE);
if (requiredTlds != null)
properties.put(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requiredTlds);
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
try
{
String originId = getOriginId(serviceRef.getBundle(), base);
ServiceApp app = new ServiceApp(getDeploymentManager(), this, serviceRef.getBundle(), properties, originId);
app.setContextPath(contextPath);
app.setWebAppPath(base);
app.setWebAppContext(webApp); //set the pre=made webapp instance
_serviceMap.put(serviceRef, app);
getDeploymentManager().addApp(app);
return true;
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
/* ------------------------------------------------------------ */
/**
* @param context the webapp
*/
public boolean serviceRemoved (ServiceReference serviceRef, ContextHandler context)
{
if (context == null || !(context instanceof WebAppContext))
return false;
String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return false; //this service represents a contexthandler that will be deregistered as a service by another of our deployers
App app = _serviceMap.remove(serviceRef);
if (app != null)
{
getDeploymentManager().removeApp(app);
return true;
}
return false;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
*/
protected void doStart() throws Exception
{
//register as an osgi service for deploying bundles, advertising the name of the jetty Server instance we are related to
Dictionary<String,String> properties = new Hashtable<String,String>();
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
//register as an osgi service for deploying contexts (discovered as osgi services), advertising the name of the jetty Server instance we are related to
_serviceRegForServices = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ServiceProvider.class.getName(), this, properties);
super.doStart();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
*/
@Override
protected void doStop() throws Exception
{
//unregister ourselves
if (_serviceRegForServices != null)
{
try
{
_serviceRegForServices.unregister();
}
catch (Exception e)
{
LOG.warn(e);
}
}
super.doStop();
}
}

View File

@ -24,6 +24,7 @@ import java.util.StringTokenizer;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.log.Log;
@ -32,6 +33,9 @@ import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
/**
* DefaultJettyAtJettyHomeHelper
*
*
* Called by the {@link JettyBootstrapActivator} during the starting of the
* bundle. If the system property 'jetty.home' is defined and points to a
* folder, then setup the corresponding jetty server.
@ -45,49 +49,20 @@ public class DefaultJettyAtJettyHomeHelper
* used to configure jetty. By default the value is 'etc/jetty.xml' when the
* path is relative the file is resolved relatively to jettyhome.
*/
public static final String SYS_PROP_JETTY_ETC_FILES = OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS;
public static final String JETTY_ETC_FILES = OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS;
/**
* Usual system property used as the hostname for a typical jetty
* configuration.
*/
public static final String SYS_PROP_JETTY_HOME = "jetty.home";
/**
* System property to point to a bundle that embeds a jetty configuration
* and that jetty configuration should be the default jetty server. First we
* look for jetty.home. If we don't find it then we look for this property.
*/
public static final String SYS_PROP_JETTY_HOME_BUNDLE = "jetty.home.bundle";
/**
* Usual system property used as the hostname for a typical jetty
* configuration.
*/
public static final String SYS_PROP_JETTY_HOST = "jetty.host";
/**
* Usual system property used as the port for http for a typical jetty
* configuration.
*/
public static final String SYS_PROP_JETTY_PORT = "jetty.port";
/**
* Usual system property used as the port for https for a typical jetty
* configuration.
*/
public static final String SYS_PROP_JETTY_PORT_SSL = "jetty.port.ssl";
/**
* Set of config files to apply to a jetty Server instance if none are supplied by SYS_PROP_JETTY_ETC_FILES
*/
public static final String DEFAULT_JETTY_ETC_FILES = "jetty.xml,jetty-selector.xml,jetty-deployer.xml";
public static final String DEFAULT_JETTY_ETC_FILES = "etc/jetty.xml,etc/jetty-selector.xml,etc/jetty-deployer.xml";
/**
* Default location within bundle of a jetty home dir.
*/
public static final String DEFAULT_JETTYHOME = "/jettyhome";
/* ------------------------------------------------------------ */
/**
* Called by the JettyBootStrapActivator. If the system property jetty.home
* is defined and points to a folder, creates a corresponding jetty
@ -112,8 +87,8 @@ public class DefaultJettyAtJettyHomeHelper
*/
public static void startJettyAtJettyHome(BundleContext bundleContext) throws Exception
{
String jettyHomeSysProp = System.getProperty(SYS_PROP_JETTY_HOME);
String jettyHomeBundleSysProp = System.getProperty(SYS_PROP_JETTY_HOME_BUNDLE);
String jettyHomeSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME);
String jettyHomeBundleSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME_BUNDLE);
File jettyHome = null;
Bundle jettyHomeBundle = null;
if (jettyHomeSysProp != null)
@ -170,17 +145,18 @@ public class DefaultJettyAtJettyHomeHelper
// these properties usually are the ones passed to this type of
// configuration.
setProperty(properties, SYS_PROP_JETTY_HOME, System.getProperty(SYS_PROP_JETTY_HOME));
setProperty(properties, SYS_PROP_JETTY_HOST, System.getProperty(SYS_PROP_JETTY_HOST));
setProperty(properties, SYS_PROP_JETTY_PORT, System.getProperty(SYS_PROP_JETTY_PORT));
setProperty(properties, SYS_PROP_JETTY_PORT_SSL, System.getProperty(SYS_PROP_JETTY_PORT_SSL));
setProperty(properties, OSGiServerConstants.JETTY_HOME, System.getProperty(OSGiServerConstants.JETTY_HOME));
setProperty(properties, OSGiServerConstants.JETTY_HOST, System.getProperty(OSGiServerConstants.JETTY_HOST));
setProperty(properties, OSGiServerConstants.JETTY_PORT, System.getProperty(OSGiServerConstants.JETTY_PORT));
setProperty(properties, OSGiServerConstants.JETTY_PORT_SSL, System.getProperty(OSGiServerConstants.JETTY_PORT_SSL));
//register the Server instance as an OSGi service.
bundleContext.registerService(Server.class.getName(), server, properties);
// hookNestedConnectorToBridgeServlet(server);
}
/* ------------------------------------------------------------ */
/**
* Minimum setup for the location of the configuration files given a
* jettyhome folder. Reads the system property jetty.etc.config.urls and
@ -192,7 +168,7 @@ public class DefaultJettyAtJettyHomeHelper
*/
private static String getJettyConfigurationURLs(File jettyhome)
{
String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES, "etc/jetty.xml");
String jettyetc = System.getProperty(JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
StringTokenizer tokenizer = new StringTokenizer(jettyetc, ";,", false);
StringBuilder res = new StringBuilder();
while (tokenizer.hasMoreTokens())
@ -214,7 +190,9 @@ public class DefaultJettyAtJettyHomeHelper
}
return res.toString();
}
/* ------------------------------------------------------------ */
/**
* Minimum setup for the location of the configuration files given a
* configuration embedded inside a bundle. Reads the system property
@ -226,7 +204,7 @@ public class DefaultJettyAtJettyHomeHelper
*/
private static String getJettyConfigurationURLs(Bundle configurationBundle)
{
String files = System.getProperty(SYS_PROP_JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
String files = System.getProperty(JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
StringTokenizer tokenizer = new StringTokenizer(files, ";,", false);
StringBuilder res = new StringBuilder();
@ -242,36 +220,39 @@ public class DefaultJettyAtJettyHomeHelper
else
{
//relative file path
Enumeration<URL> enUrls = BundleFileLocatorHelper.DEFAULT.findEntries(configurationBundle, etcFile);
Enumeration<URL> enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, etcFile);
// default for org.eclipse.osgi.boot where we look inside
// jettyhome for the default embedded configuration.
// default inside jettyhome. this way fragments to the bundle
// can define their own configuration.
if ((enUrls == null || !enUrls.hasMoreElements()))
{
String tmp = DEFAULT_JETTYHOME+"/etc/"+etcFile;
enUrls = BundleFileLocatorHelper.DEFAULT.findEntries(configurationBundle, tmp);
LOG.info("Configuring jetty with the default embedded configuration:" + "bundle: "
String tmp = DEFAULT_JETTYHOME+etcFile;
enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, tmp);
LOG.info("Configuring jetty from bundle: "
+ configurationBundle.getSymbolicName()
+ " config: "+tmp);
+ " with "+tmp);
}
if (enUrls == null || !enUrls.hasMoreElements())
{
LOG.warn("Unable to locate a jetty configuration file for " + etcFile);
throw new IllegalStateException ("Unable to locate a jetty configuration file for " + etcFile);
}
if (enUrls != null)
{
while (enUrls.hasMoreElements())
{
appendToCommaSeparatedList(res, enUrls.nextElement().toString());
URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getFileURL(enUrls.nextElement());
appendToCommaSeparatedList(res, url.toString());
}
}
}
}
return res.toString();
}
/* ------------------------------------------------------------ */
private static void appendToCommaSeparatedList(StringBuilder buffer, String value)
{
if (buffer.length() != 0)
@ -280,7 +261,9 @@ public class DefaultJettyAtJettyHomeHelper
}
buffer.append(value);
}
/* ------------------------------------------------------------ */
private static void setProperty(Dictionary<String,String> properties, String key, String value)
{
if (value != null)
@ -288,7 +271,9 @@ public class DefaultJettyAtJettyHomeHelper
properties.put(key, value);
}
}
/* ------------------------------------------------------------ */
/**
* recursively substitute the ${sysprop} by their actual system property.
* ${sysprop,defaultvalue} will use 'defaultvalue' as the value if no

View File

@ -24,16 +24,24 @@ import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.jetty.deploy.AppLifeCycle;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.bindings.StandardStarter;
import org.eclipse.jetty.deploy.bindings.StandardStopper;
import org.eclipse.jetty.osgi.boot.BundleContextProvider;
import org.eclipse.jetty.osgi.boot.BundleWebAppProvider;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
import org.eclipse.jetty.osgi.boot.OSGiDeployer;
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.osgi.boot.OSGiUndeployer;
import org.eclipse.jetty.osgi.boot.ServiceContextProvider;
import org.eclipse.jetty.osgi.boot.ServiceWebAppProvider;
import org.eclipse.jetty.osgi.boot.internal.jsp.TldLocatableURLClassloader;
import org.eclipse.jetty.osgi.boot.internal.webapp.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.boot.internal.webapp.LibExtClassLoaderHelper;
import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleDeployerHelper;
import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleTrackerCustomizer;
import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.IO;
@ -59,6 +67,8 @@ public class ServerInstanceWrapper
public static final String PROPERTY_THIS_JETTY_XML_FOLDER_URL = "this.jetty.xml.parent.folder.url";
private static Logger LOG = Log.getLogger(ServerInstanceWrapper.class.getName());
private final String _managedServerName;
@ -67,7 +77,7 @@ public class ServerInstanceWrapper
*/
private Server _server;
private ContextHandlerCollection _ctxtHandler;
private ContextHandlerCollection _ctxtCollection;
/**
* This is the class loader that should be the parent classloader of any
@ -77,21 +87,22 @@ public class ServerInstanceWrapper
private ClassLoader _commonParentClassLoaderForWebapps;
private DeploymentManager _deploymentManager;
private OSGiAppProvider _provider;
private WebBundleDeployerHelper _webBundleDeployerHelper;
/* ------------------------------------------------------------ */
public ServerInstanceWrapper(String managedServerName)
{
_managedServerName = managedServerName;
}
/* ------------------------------------------------------------ */
public String getManagedServerName()
{
return _managedServerName;
}
/* ------------------------------------------------------------ */
/**
* The classloader that should be the parent classloader for each webapp
* deployed on this server.
@ -102,7 +113,9 @@ public class ServerInstanceWrapper
{
return _commonParentClassLoaderForWebapps;
}
/* ------------------------------------------------------------ */
/**
* @return The deployment manager registered on this server.
*/
@ -110,33 +123,28 @@ public class ServerInstanceWrapper
{
return _deploymentManager;
}
/* ------------------------------------------------------------ */
/**
* @return The app provider registered on this server.
*/
public OSGiAppProvider getOSGiAppProvider()
{
return _provider;
}
public Server getServer()
{
return _server;
}
public WebBundleDeployerHelper getWebBundleDeployerHelp()
{
return _webBundleDeployerHelper;
}
/* ------------------------------------------------------------ */
/**
* @return The collection of context handlers
*/
public ContextHandlerCollection getContextHandlerCollection()
{
return _ctxtHandler;
return _ctxtCollection;
}
/* ------------------------------------------------------------ */
public void start(Server server, Dictionary props) throws Exception
{
_server = server;
@ -151,20 +159,20 @@ public class ServerInstanceWrapper
List<File> shared = sharedURLs != null ? extractFiles(sharedURLs) : null;
libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader(shared, null, server, JettyBootstrapActivator.class.getClassLoader());
if (LOG.isDebugEnabled()) LOG.debug("LibExtClassLoader = "+libExtClassLoader);
Thread.currentThread().setContextClassLoader(libExtClassLoader);
configure(server, props);
init();
// now that we have an app provider we can call the registration
// customizer.
URL[] jarsWithTlds = getJarsWithTlds();
_commonParentClassLoaderForWebapps = jarsWithTlds == null ? libExtClassLoader : new TldLocatableURLClassloader(libExtClassLoader, jarsWithTlds);
if (LOG.isDebugEnabled()) LOG.debug("common classloader = "+_commonParentClassLoaderForWebapps);
server.start();
_webBundleDeployerHelper = new WebBundleDeployerHelper(this);
}
catch (Exception e)
{
@ -187,7 +195,7 @@ public class ServerInstanceWrapper
}
}
/* ------------------------------------------------------------ */
public void stop()
{
try
@ -202,7 +210,9 @@ public class ServerInstanceWrapper
LOG.warn(e);
}
}
/* ------------------------------------------------------------ */
/**
* TODO: right now only the jetty-jsp bundle is scanned for common taglibs.
* Should support a way to plug more bundles that contain taglibs.
@ -226,11 +236,19 @@ public class ServerInstanceWrapper
*/
private URL[] getJarsWithTlds() throws Exception
{
//Jars that are added onto the equivalent of the container classpath are:
// jstl jars: identified by the class WhenTag (and the boot-bundle manifest imports the jstl packages
// bundles identified by System property org.eclipse.jetty.osgi.tldbundles
// bundle symbolic name patterns defined in the DeploymentManager
//
// Any bundles mentioned in the Require-TldBundle manifest header of the webapp bundle MUST ALSO HAVE Import-Bundle
// in order to get them onto the classpath of the webapp.
ArrayList<URL> res = new ArrayList<URL>();
WebBundleDeployerHelper.staticInit();// that is not looking great.
for (WebappRegistrationCustomizer regCustomizer : WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS)
for (WebappRegistrationCustomizer regCustomizer : WebBundleTrackerCustomizer.JSP_REGISTRATION_HELPERS)
{
URL[] urls = regCustomizer.getJarsWithTlds(_provider, WebBundleDeployerHelper.BUNDLE_FILE_LOCATOR_HELPER);
URL[] urls = regCustomizer.getJarsWithTlds(_deploymentManager, BundleFileLocatorHelperFactory.getFactory().getHelper());
for (URL url : urls)
{
if (!res.contains(url)) res.add(url);
@ -241,7 +259,9 @@ public class ServerInstanceWrapper
else
return null;
}
/* ------------------------------------------------------------ */
private void configure(Server server, Dictionary props) throws Exception
{
String jettyConfigurationUrls = (String) props.get(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS);
@ -249,15 +269,20 @@ public class ServerInstanceWrapper
if (jettyConfigurations == null || jettyConfigurations.isEmpty()) { return; }
Map<String, Object> id_map = new HashMap<String, Object>();
//TODO need to put in the id of the server being configured
//Put in a mapping for the id "Server" and the name of the server as the instance being configured
id_map.put("Server", server);
id_map.put((String)props.get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME), server);
Map<String, String> properties = new HashMap<String, String>();
Enumeration<Object> en = props.keys();
while (en.hasMoreElements())
{
Object key = en.nextElement();
Object value = props.get(key);
properties.put(String.valueOf(key), String.valueOf(value));
String keyStr = String.valueOf(key);
String valStr = String.valueOf(value);
properties.put(keyStr, valStr);
server.setAttribute(keyStr, valStr);
}
for (URL jettyConfiguration : jettyConfigurations)
@ -267,6 +292,11 @@ public class ServerInstanceWrapper
{
// Execute a Jetty configuration file
Resource r = Resource.newResource(jettyConfiguration);
if (!r.exists())
{
LOG.warn("File does not exist "+r);
continue;
}
is = r.getInputStream();
XmlConfiguration config = new XmlConfiguration(is);
config.getIdMap().putAll(id_map);
@ -309,45 +339,97 @@ public class ServerInstanceWrapper
*
* It is assumed the server has already been configured with the ContextHandlerCollection structure.
*
* The server must have an instance of OSGiAppProvider. If one is not provided, it is created.
*/
private void init()
{
// Get the context handler
_ctxtHandler = (ContextHandlerCollection) _server.getChildHandlerByClass(ContextHandlerCollection.class);
_ctxtCollection = (ContextHandlerCollection) _server.getChildHandlerByClass(ContextHandlerCollection.class);
// get a deployerManager
if (_ctxtCollection == null)
throw new IllegalStateException("ERROR: No ContextHandlerCollection configured in Server");
List<String> providerClassNames = new ArrayList<String>();
// get a deployerManager and some providers
List<DeploymentManager> deployers = _server.getBeans(DeploymentManager.class);
if (deployers != null && !deployers.isEmpty())
{
_deploymentManager = deployers.get(0);
for (AppProvider provider : _deploymentManager.getAppProviders())
{
if (provider instanceof OSGiAppProvider)
{
_provider = (OSGiAppProvider) provider;
break;
}
providerClassNames.add(provider.getClass().getName());
}
if (_provider == null)
}
else
{
//add some kind of default
_deploymentManager = new DeploymentManager();
_deploymentManager.setContexts(_ctxtCollection);
_server.addBean(_deploymentManager);
}
_deploymentManager.setUseStandardBindings(false);
List<AppLifeCycle.Binding> deploymentLifeCycleBindings = new ArrayList<AppLifeCycle.Binding>();
deploymentLifeCycleBindings.add(new OSGiDeployer());
deploymentLifeCycleBindings.add(new StandardStarter());
deploymentLifeCycleBindings.add(new StandardStopper());
deploymentLifeCycleBindings.add(new OSGiUndeployer());
_deploymentManager.setLifeCycleBindings(deploymentLifeCycleBindings);
if (!providerClassNames.contains(BundleWebAppProvider.class.getName()))
{
// create it on the fly with reasonable default values.
try
{
// create it on the fly with reasonable default values.
try
{
_provider = new OSGiAppProvider();
_provider.setMonitoredDirResource(Resource.newResource(getDefaultOSGiContextsHome(new File(System.getProperty("jetty.home"))).toURI()));
}
catch (IOException e)
{
LOG.warn(e);
}
_deploymentManager.addAppProvider(_provider);
BundleWebAppProvider webAppProvider = new BundleWebAppProvider(this);
_deploymentManager.addAppProvider(webAppProvider);
}
catch (Exception e)
{
LOG.warn(e);
}
}
if (_ctxtHandler == null || _provider == null) throw new IllegalStateException("ERROR: No ContextHandlerCollection or OSGiAppProvider configured");
if (!providerClassNames.contains(ServiceWebAppProvider.class.getName()))
{
// create it on the fly with reasonable default values.
try
{
ServiceWebAppProvider webAppProvider = new ServiceWebAppProvider(this);
_deploymentManager.addAppProvider(webAppProvider);
}
catch (Exception e)
{
LOG.warn(e);
}
}
if (!providerClassNames.contains(BundleContextProvider.class.getName()))
{
try
{
BundleContextProvider contextProvider = new BundleContextProvider(this);
_deploymentManager.addAppProvider(contextProvider);
}
catch (Exception e)
{
LOG.warn(e);
}
}
if (!providerClassNames.contains(ServiceContextProvider.class.getName()))
{
try
{
ServiceContextProvider contextProvider = new ServiceContextProvider(this);
_deploymentManager.addAppProvider(contextProvider);
}
catch (Exception e)
{
LOG.warn(e);
}
}
}
/**
@ -374,10 +456,6 @@ public class ServerInstanceWrapper
return new File(jettyHome, "/contexts");
}
File getOSGiContextsHome()
{
return _provider.getContextXmlDirAsFile();
}
/**
* @return the urls in this string.
@ -391,7 +469,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 +493,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);

View File

@ -0,0 +1,53 @@
// ========================================================================
// 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 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;
}
}

View File

@ -14,12 +14,18 @@ package org.eclipse.jetty.osgi.boot.internal.webapp;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.jetty.osgi.boot.BundleWebAppProvider;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.ServiceProvider;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.IManagedJettyServerRegistry;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
@ -34,107 +40,83 @@ import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
/**
* When a {@link ContextHandler} service is activated we look into it and if the
* corresponding webapp is actually not configured then we go and register it.
* <p>
* The idea is to always go through this class when we deploy a new webapp on
* jetty.
* </p>
* <p>
* We are exposing each web-application as an OSGi service. This lets us update
* the webapps and stop/start them directly at the OSGi layer. It also give us
* many ways to declare those services: Declarative Services for example. <br/>
* It is a bit different from the way the HttpService works where we would have
* a WebappService and we woud register a webapp onto it. <br/>
* It does not go against RFC-66 nor does it prevent us from supporting the
* WebappContainer semantics.
* </p>
* JettyContextHandlerServiceTracker
*
* When a {@link ContextHandler} is activated as an osgi service we find a jetty deployer
* for it. The ContextHandler could be either a WebAppContext or any other derivative of
* ContextHandler.
*
* ContextHandlers and WebApps can also be deployed into jetty without creating them as
* osgi services. Instead, they can be deployed via manifest headers inside bundles. See
* {@link WebBundleTrackerCustomizer}.
*/
public class JettyContextHandlerServiceTracker implements ServiceListener
{
private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName());
/** New style: ability to manage multiple jetty instances */
private final IManagedJettyServerRegistry _registry;
/** The context-handler to deactivate indexed by context handler */
private Map<ServiceReference, ContextHandler> _indexByServiceReference = new HashMap<ServiceReference, ContextHandler>();
/**
* The index is the bundle-symbolic-name/path/to/context/file when there is
* such thing
*/
private Map<String, ServiceReference> _indexByContextFile = new HashMap<String, ServiceReference>();
/** in charge of detecting changes in the osgi contexts home folder. */
private Scanner _scanner;
private static Logger LOG = Log.getLogger(JettyContextHandlerServiceTracker.class);
public static final String FILTER = "(objectclass=" + ServiceProvider.class.getName() + ")";
//track all instances of deployers of webapps as bundles
ServiceTracker _serviceTracker;
/* ------------------------------------------------------------ */
/**
* @param registry
*/
public JettyContextHandlerServiceTracker(IManagedJettyServerRegistry registry) throws Exception
public JettyContextHandlerServiceTracker() throws Exception
{
_registry = registry;
//track all instances of deployers of webapps
Bundle myBundle = FrameworkUtil.getBundle(this.getClass());
_serviceTracker = new ServiceTracker(myBundle.getBundleContext(), FrameworkUtil.createFilter(FILTER),null);
_serviceTracker.open();
}
public void stop() throws Exception
{
if (_scanner != null)
{
_scanner.stop();
}
// the class that created the server is also in charge of stopping it.
// nothing to stop in the WebappRegistrationHelper
}
/* ------------------------------------------------------------ */
/**
* @param contextHome Parent folder where the context files can override the
* context files defined in the web bundles: equivalent to the
* contexts folder in a traditional jetty installation. when
* null, just do nothing.
* @param managedServerName
* @return
*/
protected void setupContextHomeScanner(File contextHome) throws IOException
public Map<ServiceReference, ServiceProvider> getDeployers(String managedServerName)
{
if (contextHome == null) { return; }
final String osgiContextHomeFolderCanonicalPath = contextHome.getCanonicalPath();
_scanner = new Scanner();
_scanner.setRecursive(true);
_scanner.setReportExistingFilesOnStartup(false);
_scanner.addListener(new Scanner.DiscreteListener()
if (managedServerName == null)
managedServerName = OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME;
Map<ServiceReference, ServiceProvider> candidates = new HashMap<ServiceReference, ServiceProvider>();
ServiceReference[] references = _serviceTracker.getServiceReferences();
if (references != null)
{
public void fileAdded(String filename) throws Exception
for (ServiceReference ref:references)
{
// adding a file does not create a new app,
// it just reloads it with the new custom file.
// well, if the file does not define a context handler,
// then in fact it does remove it.
reloadJettyContextHandler(filename, osgiContextHomeFolderCanonicalPath);
String name = (String)ref.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
if (managedServerName.equalsIgnoreCase(name))
{
ServiceProvider candidate = (ServiceProvider)_serviceTracker.getService(ref);
if (candidate != null)
candidates.put(ref, candidate);
}
}
public void fileChanged(String filename) throws Exception
{
reloadJettyContextHandler(filename, osgiContextHomeFolderCanonicalPath);
}
public void fileRemoved(String filename) throws Exception
{
// removing a file does not remove the app:
// it just goes back to the default embedded in the bundle.
// well, if there was no default then it does remove it.
reloadJettyContextHandler(filename, osgiContextHomeFolderCanonicalPath);
}
});
}
return candidates;
}
/* ------------------------------------------------------------ */
/**
* Receives notification that a service has had a lifecycle change.
*
* @param ev The <code>ServiceEvent</code> object.
*/
/**
* @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
*/
public void serviceChanged(ServiceEvent ev)
{
ServiceReference sr = ev.getServiceReference();
@ -143,16 +125,32 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
case ServiceEvent.MODIFIED:
case ServiceEvent.UNREGISTERING:
{
ContextHandler ctxtHandler = unregisterInIndex(ev.getServiceReference());
if (ctxtHandler != null && !ctxtHandler.isStopped())
BundleContext context = FrameworkUtil.getBundle(JettyBootstrapActivator.class).getBundleContext();
ContextHandler contextHandler = (ContextHandler) context.getService(sr);
//if this was not a service that another of our deployers may have deployed (in which case they will undeploy it)
String watermark = (String)sr.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
{
try
//Get a jetty deployer targetted to the named server instance, or the default one if not named
String serverName = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
Map<ServiceReference, ServiceProvider> candidates = getDeployers(serverName);
if (candidates != null)
{
getWebBundleDeployerHelp(sr).unregister(ctxtHandler);
}
catch (Exception e)
{
__logger.warn(e);
boolean removed = false;
Iterator<Entry<ServiceReference, ServiceProvider>> itor = candidates.entrySet().iterator();
while (!removed && itor.hasNext())
{
Entry<ServiceReference, ServiceProvider> e = itor.next();
try
{
removed = e.getValue().serviceRemoved(sr, contextHandler);
}
catch (Exception x)
{
LOG.warn("Error undeploying service representing jetty context ", x);
}
}
}
}
}
@ -175,210 +173,34 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
// is configured elsewhere.
return;
}
String contextFilePath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
if (contextHandler instanceof WebAppContext && contextFilePath == null)
// it could be a web-application that will in fact be configured
// via a context file.
// that case is identified by the fact that the contextFilePath
// is not null
// in that case we must use the register context methods.
String watermark = (String)sr.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return; //another of our deployers is responsible for handling service registrations for this context
//Get a jetty deployer targetted to the named server instance, or the default one if not named
String serverName = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
Map<ServiceReference, ServiceProvider> candidates = getDeployers(serverName);
if (candidates != null)
{
WebAppContext webapp = (WebAppContext) contextHandler;
String contextPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH);
if (contextPath == null)
boolean added = false;
Iterator<Entry<ServiceReference, ServiceProvider>> itor = candidates.entrySet().iterator();
while (!added && itor.hasNext())
{
contextPath = webapp.getContextPath();
}
String webXmlPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WEB_XML_PATH);
if (webXmlPath == null)
{
webXmlPath = webapp.getDescriptor();
}
String defaultWebXmlPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_DEFAULT_WEB_XML_PATH);
if (defaultWebXmlPath == null)
{
String jettyHome = System.getProperty(DefaultJettyAtJettyHomeHelper.SYS_PROP_JETTY_HOME);
if (jettyHome != null)
Entry<ServiceReference, ServiceProvider> e = itor.next();
try
{
File etc = new File(jettyHome, "etc");
if (etc.exists() && etc.isDirectory())
{
File webDefault = new File(etc, "webdefault.xml");
if (webDefault.exists())
defaultWebXmlPath = webDefault.getAbsolutePath();
else
defaultWebXmlPath = webapp.getDefaultsDescriptor();
}
else
defaultWebXmlPath = webapp.getDefaultsDescriptor();
added = e.getValue().serviceAdded(sr, contextHandler);
if (added && LOG.isDebugEnabled())
LOG.debug("Provider "+e.getValue()+" deployed "+contextHandler);
}
}
String war = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WAR);
try
{
IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr);
if (deployerHelper == null)
catch (Exception x)
{
LOG.warn("Error deploying service representing jetty context", x);
}
else
{
WebAppContext handler = deployerHelper.registerWebapplication(contributor,
war,
contextPath,
(String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH),
(String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE),
(String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE),
webXmlPath, defaultWebXmlPath, webapp);
if (handler != null)
{
registerInIndex(handler, sr);
}
}
}
catch (Throwable e)
{
__logger.warn(e);
}
}
else
{
// consider this just an empty skeleton:
if (contextFilePath == null) { throw new IllegalArgumentException("the property contextFilePath is required"); }
try
{
IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr);
if (deployerHelper == null)
{
// more warnings?
}
else
{
if (Boolean.TRUE.toString().equals(sr.getProperty(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE)))
{
contextHandler = null;
}
ContextHandler handler = deployerHelper.registerContext(contributor,
contextFilePath,
(String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH),
(String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE),
(String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE),
contextHandler);
if (handler != null)
{
registerInIndex(handler, sr);
}
}
}
catch (Throwable e)
{
__logger.warn(e);
}
}
break;
}
break;
}
}
private void registerInIndex(ContextHandler handler, ServiceReference sr)
{
_indexByServiceReference.put(sr, handler);
String key = getSymbolicNameAndContextFileKey(sr);
if (key != null)
{
_indexByContextFile.put(key, sr);
}
}
/**
* Returns the ContextHandler to stop.
*
* @param reg
* @return the ContextHandler to stop.
*/
private ContextHandler unregisterInIndex(ServiceReference sr)
{
ContextHandler handler = _indexByServiceReference.remove(sr);
String key = getSymbolicNameAndContextFileKey(sr);
if (key != null)
{
_indexByContextFile.remove(key);
}
if (handler == null)
{
// a warning?
return null;
}
return handler;
}
/**
* @param sr
* @return The key for a context file within the osgi contexts home folder.
*/
private String getSymbolicNameAndContextFileKey(ServiceReference sr)
{
String contextFilePath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
if (contextFilePath != null) { return sr.getBundle().getSymbolicName() + "/" + contextFilePath; }
return null;
}
/**
* Called by the scanner when one of the context files is changed.
*
* @param contextFileFully
*/
public void reloadJettyContextHandler(String canonicalNameOfFileChanged, String osgiContextHomeFolderCanonicalPath)
{
String key = getNormalizedRelativePath(canonicalNameOfFileChanged, osgiContextHomeFolderCanonicalPath);
if (key == null) { return; }
ServiceReference sr = _indexByContextFile.get(key);
if (sr == null)
{
// nothing to do?
return;
}
serviceChanged(new ServiceEvent(ServiceEvent.MODIFIED, sr));
}
/**
* @param canFilename
* @return
*/
private String getNormalizedRelativePath(String canFilename, String osgiContextHomeFolderCanonicalPath)
{
if (!canFilename.startsWith(osgiContextHomeFolderCanonicalPath))
{
// why are we here: this does not look like a child of the osgi
// contexts home.
// warning?
return null;
}
return canFilename.substring(osgiContextHomeFolderCanonicalPath.length()).replace('\\', '/');
}
/**
* @return The server on which this webapp is meant to be deployed
*/
private ServerInstanceWrapper getServerInstanceWrapper(String managedServerName)
{
if (_registry == null) { return null; }
if (managedServerName == null)
{
managedServerName = OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME;
}
ServerInstanceWrapper wrapper = _registry.getServerInstanceWrapper(managedServerName);
// System.err.println("Returning " + managedServerName + " = " +
// wrapper);
return wrapper;
}
private IWebBundleDeployerHelper getWebBundleDeployerHelp(ServiceReference sr)
{
if (_registry == null) { return null; }
String managedServerName = (String) sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
ServerInstanceWrapper wrapper = getServerInstanceWrapper(managedServerName);
return wrapper != null ? wrapper.getWebBundleDeployerHelp() : null;
}
}

View File

@ -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);
}
/**

View File

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

View File

@ -12,46 +12,61 @@
// ========================================================================
package org.eclipse.jetty.osgi.boot.internal.webapp;
import java.net.URL;
import java.util.Dictionary;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.BundleProvider;
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.FrameworkUtil;
import org.osgi.util.tracker.BundleTracker;
import org.osgi.util.tracker.BundleTrackerCustomizer;
import org.osgi.util.tracker.ServiceTracker;
/**
* Support bundles that declare the webapp directly through headers in their
* manifest.
* <p>
* Those headers will define a new WebApplication:
* <ul>
* <li>Web-ContextPath</li>
* <li>Jetty-WarFolderPath</li>
* </ul>
* </p>
* <p>
* Those headers will define a new app started via a jetty-context or a list of
* them. ',' column is the separator between the various context files.
* <ul>
* <li>Jetty-ContextFilePath</li>
* </ul>
* </p>
* And generate a jetty WebAppContext or another ContextHandler then registers
* it as service. Kind of simpler than declarative services and their xml files.
* Also avoid having the contributing bundle depend on jetty's package for
* WebApp.
* WebBundleTrackerCustomizer
*
*
* Support bundles that declare a webpp or context directly through headers in their
* manifest. They will be deployed to the default jetty Server instance.
*
* If you wish to deploy a context or webapp to a different jetty Server instance,
* register your context/webapp as an osgi service, and set the property OSGiServerConstants.MANAGED_JETTY_SERVER_NAME
* with the name of the Server instance you wish to depoy to.
*
* @author hmalphettes
*/
public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
{
private static final Logger LOG = Log.getLogger(WebBundleTrackerCustomizer.class);
public static Collection<WebappRegistrationCustomizer> JSP_REGISTRATION_HELPERS = new ArrayList<WebappRegistrationCustomizer>();
public static final String FILTER = "(&(objectclass=" + BundleProvider.class.getName() + ")"+
"("+OSGiServerConstants.MANAGED_JETTY_SERVER_NAME+"="+OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME+"))";
private ServiceTracker _serviceTracker;
/* ------------------------------------------------------------ */
/**
* @throws Exception
*/
public WebBundleTrackerCustomizer ()
throws Exception
{
Bundle myBundle = FrameworkUtil.getBundle(this.getClass());
//track all instances of deployers of webapps/contexts as bundles
_serviceTracker = new ServiceTracker(myBundle.getBundleContext(), FrameworkUtil.createFilter(FILTER),null);
_serviceTracker.open();
}
/* ------------------------------------------------------------ */
/**
* A bundle is being added to the <code>BundleTracker</code>.
*
@ -76,8 +91,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)
{
@ -91,6 +105,8 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
return null;
}
/* ------------------------------------------------------------ */
/**
* A bundle tracked by the <code>BundleTracker</code> has been modified.
*
@ -118,6 +134,8 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
}
}
/* ------------------------------------------------------------ */
/**
* A bundle tracked by the <code>BundleTracker</code> has been removed.
*
@ -136,129 +154,64 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
unregister(bundle);
}
/* ------------------------------------------------------------ */
/**
* @param bundle
* @return true if this bundle in indeed a web-bundle.
*/
private boolean register(Bundle bundle)
{
Dictionary<?, ?> dic = bundle.getHeaders();
String warFolderRelativePath = (String) dic.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
if (warFolderRelativePath != null)
if (bundle == null)
return false;
//It might be a bundle that we can deploy to our default jetty server instance
boolean deployed = false;
Object[] deployers = _serviceTracker.getServices();
if (deployers != null)
{
String contextPath = getWebContextPath(bundle, dic, false);
if (contextPath == null || !contextPath.startsWith("/"))
int i=0;
while (!deployed && i<deployers.length)
{
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.
BundleProvider p = (BundleProvider)deployers[i];
try
{
deployed = p.bundleAdded(bundle);
}
catch (Exception x)
{
LOG.warn("Error deploying bundle for jetty context", x);
}
i++;
}
}
else if (dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH) != null)
return deployed;
}
/* ------------------------------------------------------------ */
/**
* @param bundle
*/
private void unregister(Bundle bundle)
{
Object[] deployers = _serviceTracker.getServices();
boolean undeployed = false;
if (deployers != null)
{
String contextFileRelativePath = (String) dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
if (contextFileRelativePath == null)
{
// nothing to register here.
return false;
}
// support for multiple webapps in the same bundle:
String[] pathes = contextFileRelativePath.split(",;");
for (String path : pathes)
int i=0;
while (!undeployed && i<deployers.length)
{
try
{
JettyBootstrapActivator.registerContext(bundle, path.trim());
undeployed = ((BundleProvider)deployers[i++]).bundleRemoved(bundle);
}
catch (Throwable e)
catch (Exception x)
{
LOG.warn(e);
LOG.warn("Error undeploying bundle for jetty context", x);
}
}
return true;
}
else
{
// support for OSGi-RFC66; disclaimer, no access to the actual
// (draft) of the spec: just a couple of posts on the
// world-wide-web.
URL rfc66Webxml = bundle.getEntry("/WEB-INF/web.xml");
if (rfc66Webxml == null && dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) == null)
{
return false;// no webapp in here
}
// this is risky: should we make sure that there is no classes and
// jars directly available
// at the root of the of the bundle: otherwise they are accessible
// through the browser. we should enforce that the whole classpath
// is
// pointing to files and folders inside WEB-INF. We should
// filter-out
// META-INF too
String rfc66ContextPath = getWebContextPath(bundle, dic, rfc66Webxml == null);
try
{
JettyBootstrapActivator.registerWebapplication(bundle, ".", rfc66ContextPath);
return true;
}
catch (Throwable e)
{
LOG.warn(e);
return true;// maybe it did not work maybe it did. safer to track this bundle.
}
}
}
private String getWebContextPath(Bundle bundle, Dictionary<?, ?> dic, boolean webinfWebxmlExists)
{
String rfc66ContextPath = (String) dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (rfc66ContextPath == null)
{
if (!webinfWebxmlExists) { return null; }
// extract from the last token of the bundle's location:
// (really ?
// could consider processing the symbolic name as an alternative
// the location will often reflect the version.
// maybe this is relevant when the file is a war)
String location = bundle.getLocation();
String toks[] = location.replace('\\', '/').split("/");
rfc66ContextPath = toks[toks.length - 1];
// remove .jar, .war etc:
int lastDot = rfc66ContextPath.lastIndexOf('.');
if (lastDot != -1)
{
rfc66ContextPath = rfc66ContextPath.substring(0, lastDot);
}
}
if (!rfc66ContextPath.startsWith("/"))
{
rfc66ContextPath = "/" + rfc66ContextPath;
}
return rfc66ContextPath;
}
private void unregister(Bundle bundle)
{
// nothing to do: when the bundle is stopped, each one of its service
// reference is also stopped and that is what we use to stop the
// corresponding
// webapps registered in that bundle.
}
}

View File

@ -0,0 +1,56 @@
// ========================================================================
// 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 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;
}
}

View File

@ -83,5 +83,31 @@ public interface BundleFileLocatorHelper
* @return null or all the entries found for that path.
*/
public Enumeration<URL> findEntries(Bundle bundle, String entryPath);
/**
* Only useful for equinox: on felix we get the file:// or jar:// url
* already. Other OSGi implementations have not been tested
* <p>
* Get a URL to the bundle entry that uses a common protocol (i.e. file:
* jar: or http: etc.).
* </p>
*
* @return a URL to the bundle entry that uses a common protocol
*/
public URL getLocalURL(URL url);
/**
* Only useful for equinox: on felix we get the file:// url already. Other
* OSGi implementations have not been tested
* <p>
* Get a URL to the content of the bundle entry that uses the file:
* protocol. The content of the bundle entry may be downloaded or extracted
* to the local file system in order to create a file: URL.
*
* @return a URL to the content of the bundle entry that uses the file:
* protocol
* </p>
*/
public URL getFileURL(URL url);
}

View File

@ -0,0 +1,85 @@
// ========================================================================
// 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 org.eclipse.jetty.osgi.boot.utils;
import java.util.Dictionary;
import java.util.Hashtable;
import javax.security.auth.login.FailedLoginException;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
public class EventSender
{
//OSGi Event Admin events for webapps
public static final String DEPLOYING_EVENT = "org/osgi/service/web/DEPLOYING";
public static final String DEPLOYED_EVENT = "org/osgi/service/web/DEPLOYED";
public static final String UNDEPLOYING_EVENT = "org/osgi/service/web/UNDEPLOYING";
public static final String UNDEPLOYED_EVENT = "org/osgi/service/web/UNDEPLOYED";
public static final String FAILED_EVENT = "org/osgi/service/web/FAILED";
private static final EventSender __instance = new EventSender();
private Bundle _myBundle;
private EventAdmin _eventAdmin;
private EventSender ()
{
_myBundle = FrameworkUtil.getBundle(EventSender.class);
ServiceReference ref = _myBundle.getBundleContext().getServiceReference(EventAdmin.class.getName());
if (ref != null)
_eventAdmin = (EventAdmin)_myBundle.getBundleContext().getService(ref);
}
public static EventSender getInstance()
{
return __instance;
}
public void send (String topic, Bundle wab, String contextPath)
{
if (topic==null || wab==null || contextPath==null)
return;
send(topic, wab, contextPath, null);
}
public void send (String topic, Bundle wab, String contextPath, Exception ex)
{
if (_eventAdmin == null)
return;
Dictionary<String,Object> props = new Hashtable<String,Object>();
props.put("bundle.symbolicName", wab.getSymbolicName());
props.put("bundle.id", wab.getBundleId());
props.put("bundle", wab);
props.put("bundle.version", wab.getVersion());
props.put("context.path", contextPath);
props.put("timestamp", System.currentTimeMillis());
props.put("extender.bundle", _myBundle);
props.put("extender.bundle.symbolicName", _myBundle.getSymbolicName());
props.put("extender.bundle.id", _myBundle.getBundleId());
props.put("extender.bundle.version", _myBundle.getVersion());
if (FAILED_EVENT.equalsIgnoreCase(topic) && ex != null)
props.put("exception", ex);
_eventAdmin.sendEvent(new Event(topic, props));
}
}

View File

@ -0,0 +1,215 @@
// ========================================================================
// 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 org.eclipse.jetty.osgi.boot.utils;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.osgi.framework.Bundle;
/**
* OSGiClassLoader
*
* Class loader that is aware of a bundle. Similar to WebAppClassLoader from Jetty
* and the OSGiWebAppClassLoader, but works without webapps.
*/
public class OSGiClassLoader extends URLClassLoader
{
private static final Logger LOG = Log.getLogger(OSGiClassLoader.class);
private Bundle _bundle;
private ClassLoader _osgiBundleClassLoader;
private boolean _lookInOsgiFirst = true;
private ClassLoader _parent;
/* ------------------------------------------------------------ */
public OSGiClassLoader(ClassLoader parent, Bundle bundle)
{
super(new URL[]{}, parent);
_parent = getParent();
_bundle = bundle;
_osgiBundleClassLoader = BundleClassLoaderHelperFactory.getFactory().getHelper().getBundleClassLoader(_bundle);
}
/* ------------------------------------------------------------ */
/**
* Get a resource from the classloader
*
* Copied from WebAppClassLoader
*/
public URL getResource(String name)
{
URL url= null;
boolean tried_parent= false;
if (_parent!=null && !_lookInOsgiFirst)
{
tried_parent= true;
if (_parent!=null)
url= _parent.getResource(name);
}
if (url == null)
{
url = _osgiBundleClassLoader.getResource(name);
if (url == null && name.startsWith("/"))
{
if (LOG.isDebugEnabled())
LOG.debug("HACK leading / off " + name);
url = _osgiBundleClassLoader.getResource(name.substring(1));
}
}
if (url == null && !tried_parent)
{
if (_parent!=null)
url= _parent.getResource(name);
}
if (url != null)
if (LOG.isDebugEnabled())
LOG.debug("getResource("+name+")=" + url);
return url;
}
/* ------------------------------------------------------------ */
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException
{
return loadClass(name, false);
}
/* ------------------------------------------------------------ */
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
Class<?> c = findLoadedClass(name);
ClassNotFoundException ex= null;
boolean tried_parent= false;
if (c == null && _parent!=null && !_lookInOsgiFirst)
{
tried_parent= true;
try
{
c= _parent.loadClass(name);
if (LOG.isDebugEnabled())
LOG.debug("loaded " + c);
}
catch (ClassNotFoundException e)
{
ex= e;
}
}
if (c == null)
{
try
{
c= this.findClass(name);
}
catch (ClassNotFoundException e)
{
ex= e;
}
}
if (c == null && _parent!=null && !tried_parent)
c = _parent.loadClass(name);
if (c == null)
throw ex;
if (resolve)
resolveClass(c);
if (LOG.isDebugEnabled())
LOG.debug("loaded " + c+ " from "+c.getClassLoader());
return c;
}
/* ------------------------------------------------------------ */
@Override
public Enumeration<URL> getResources(String name) throws IOException
{
Enumeration<URL> osgiUrls = _osgiBundleClassLoader.getResources(name);
Enumeration<URL> urls = super.getResources(name);
if (_lookInOsgiFirst)
{
return Collections.enumeration(toList(osgiUrls, urls));
}
else
{
return Collections.enumeration(toList(urls, osgiUrls));
}
}
/* ------------------------------------------------------------ */
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
try
{
return _lookInOsgiFirst ? _osgiBundleClassLoader.loadClass(name) : super.findClass(name);
}
catch (ClassNotFoundException cne)
{
try
{
return _lookInOsgiFirst ? super.findClass(name) : _osgiBundleClassLoader.loadClass(name);
}
catch (ClassNotFoundException cne2)
{
throw cne;
}
}
}
/* ------------------------------------------------------------ */
/**
* @param e
* @param e2
* @return
*/
private List<URL> toList(Enumeration<URL> e, Enumeration<URL> e2)
{
List<URL> list = new ArrayList<URL>();
while (e != null && e.hasMoreElements())
list.add(e.nextElement());
while (e2 != null && e2.hasMoreElements())
list.add(e2.nextElement());
return list;
}
}

View File

@ -14,7 +14,8 @@ package org.eclipse.jetty.osgi.boot.utils;
import java.net.URL;
import org.eclipse.jetty.osgi.boot.OSGiAppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
/**
* Fix various shortcomings with the way jasper parses the tld files.
@ -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;
}

View File

@ -144,7 +144,6 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
{
// observed this on felix-2.0.0
String location = bundle.getLocation();
// System.err.println("location " + location);
if (location.startsWith("file:/"))
{
URI uri = new URI(URIUtil.encodePath(location));
@ -292,7 +291,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 +327,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
* protocol
* </p>
*/
public static URL getFileURL(URL url)
public URL getFileURL(URL url)
{
if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
{

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
<version>7.6.4-SNAPSHOT</version>
<version>7.6.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -25,6 +25,8 @@
<module>jetty-osgi-boot-warurl</module>
<module>jetty-osgi-httpservice</module>
<module>jetty-osgi-equinoxtools</module>
<module>test-jetty-osgi-webapp</module>
<module>test-jetty-osgi-context</module>
<module>test-jetty-osgi</module>
</modules>
<build>
@ -72,6 +74,11 @@
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-continuation</artifactId>

View File

@ -0,0 +1,132 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
<version>7.6.6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-jetty-osgi-context</artifactId>
<name>Jetty :: OSGi :: Context</name>
<description>Test Jetty OSGi bundle with a ContextHandler</description>
<properties>
<bundle-symbolic-name>${project.groupId}.testcontext</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/context</directory>
</resource>
</resources>
<plugins>
<!--
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>process-resources</phase>
<configuration>
<tasks>
<copy todir="target/classes/jettyhome">
<fileset dir="jettyhome">
<exclude name="**/*.log" />
</fileset>
</copy>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>artifact-jar</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
<execution>
<id>test-jar</id>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.osgi.testcontext;singleton:=true</Bundle-SymbolicName>
<Bundle-Name>Jetty OSGi Test Context</Bundle-Name>
<Bundle-Activator>com.acme.osgi.Activator</Bundle-Activator>
<Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
<!-- disable the uses directive: jetty will accomodate pretty much any versions
of the packages it uses; no need to reflect some tight dependency determined at
compilation time. -->
<_nouses>true</_nouses>
<Import-Package>
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,
*
</Import-Package>
<DynamicImport-Package>org.eclipse.jetty.*;version="[7.6,8)"</DynamicImport-Package>
<!--Require-Bundle/-->
<!-- Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment -->
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
<!-- Get root for static content, could be on file system or this bundle -->
<Call id="res" class="org.eclipse.jetty.util.resource.Resource" name="newResource">
<Arg><Property name="bundle.root"/></Arg>
</Call>
<Ref id="res">
<Call id="base" name="addPath">
<Arg>/static/</Arg>
</Call>
</Ref>
<Set name="contextPath">/unset</Set>
<!-- Set up the base resource for static files relative to inside bundle -->
<Set name="baseResource">
<Ref id="base"/>
</Set>
<Set name="handler">
<New class="org.eclipse.jetty.server.handler.ResourceHandler">
<Set name="welcomeFiles">
<Array type="String">
<Item>index.html</Item>
</Array>
</Set>
<Set name="cacheControl">max-age=3600,public</Set>
</New>
</Set>
</Configure>

View File

@ -0,0 +1,58 @@
// ========================================================================
// 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.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 ContextHandler
*
*
*/
public class Activator implements BundleActivator
{
/**
*
* @param context
*/
public void start(BundleContext context) throws Exception
{
ContextHandler ch = new ContextHandler();
Dictionary props = new Hashtable();
props.put("contextPath","/acme");
props.put("Jetty-ContextFilePath", "acme.xml");
context.registerService(ContextHandler.class.getName(),ch,props);
}
/**
* Stop the activator.
*
* @see
* org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception
{
}
}

View File

@ -0,0 +1,6 @@
<html>
<body>
<h1>Test OSGi Context</h1>
<p>ContextHandler registered as a service successfully deployed.</p>
</body>
</html>

View File

@ -0,0 +1,129 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
<version>7.6.6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-jetty-osgi-webapp</artifactId>
<name>Jetty :: OSGi :: WebApp</name>
<description>Test Jetty OSGi Webapp bundle</description>
<properties>
<bundle-symbolic-name>${project.groupId}.webapp</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<!--
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>process-resources</phase>
<configuration>
<tasks>
<copy todir="target/classes/jettyhome">
<fileset dir="jettyhome">
<exclude name="**/*.log" />
</fileset>
</copy>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>artifact-jar</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
<execution>
<id>test-jar</id>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.osgi.testapp;singleton:=true</Bundle-SymbolicName>
<Bundle-Name>Jetty OSGi Test WebApp</Bundle-Name>
<Bundle-Activator>com.acme.osgi.Activator</Bundle-Activator>
<Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
<!-- disable the uses directive: jetty will accomodate pretty much any versions
of the packages it uses; no need to reflect some tight dependency determined at
compilation time. -->
<_nouses>true</_nouses>
<Import-Package>
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,
*
</Import-Package>
<DynamicImport-Package>org.eclipse.jetty.*;version="[7.6,8)"</DynamicImport-Package>
<!--Require-Bundle/-->
<!-- Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment -->
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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
{
}
}

View File

@ -0,0 +1,6 @@
<html>
<body>
<h1>Test OSGi WebApp</h1>
<p>Webapp registered by bundle as service successfully deployed.</p>
</body>
</html>

View File

@ -134,6 +134,24 @@
<scope>test</scope>
</dependency>
<!-- test osgi webapp service -->
<dependency>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>test-jetty-osgi-webapp</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<!-- test osgi contexthandler service -->
<dependency>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>test-jetty-osgi-context</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam</artifactId>
@ -157,6 +175,12 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,43 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Configure the deployment manager -->
<!-- =========================================================== -->
<Call name="addBean">
<Arg>
<New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
<Set name="contexts">
<Ref id="Contexts" />
</Set>
<Call name="setContextAttribute">
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
<Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
</Call>
<!-- Providers of OSGi Apps -->
<!-- Call name="addAppProvider" -->
<!-- Arg -->
<!-- New class="org.eclipse.jetty.osgi.boot.OSGiAppProvider" -->
<!--
<Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
-->
<!--
<Set name="scanInterval">5</Set>
<Set name="contextXmlDir"><Property name="jetty.home" default="." />/contexts</Set>
-->
<!-- comma separated list of bundle symbolic names that contain custom tag libraries (*.tld files) -->
<!-- if those bundles don't exist or can't be loaded no errors or warning will be issued! -->
<!-- This default value plugs in the tld files of the reference implementation of JSF -->
<!--
<Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldbundles" default="javax.faces.jsf-impl" /></Set>
</New>
</Arg>
</Call>
-->
</New>
</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Add connector -->
<!-- =========================================================== -->
<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
<Set name="host"><Property name="jetty.host" /></Set>
<Set name="port"><Property name="jetty.port" default="8080"/></Set>
<Set name="maxIdleTime">300000</Set>
<Set name="Acceptors">2</Set>
<Set name="statsOn">false</Set>
<Set name="confidentialPort">8443</Set>
<Set name="lowResourcesConnections">20000</Set>
<Set name="lowResourcesMaxIdleTime">5000</Set>
</New>
</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,72 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<!-- =============================================================== -->
<!-- Configure the Jetty Server -->
<!-- -->
<!-- Documentation of this file format can be found at: -->
<!-- http://wiki.eclipse.org/Jetty/Reference/jetty.xml_syntax -->
<!-- =============================================================== -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Server Thread Pool -->
<!-- =========================================================== -->
<Set name="ThreadPool">
<!-- Default queued blocking threadpool -->
<New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
<Set name="minThreads">10</Set>
<Set name="maxThreads">200</Set>
<Set name="detailedDump">false</Set>
</New>
</Set>
<!-- =========================================================== -->
<!-- Set handler Collection Structure -->
<!-- =========================================================== -->
<Set name="handler">
<New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
<Set name="handlers">
<Array type="org.eclipse.jetty.server.Handler">
<Item>
<New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
</Item>
<Item>
<New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
</Item>
<Item>
<New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
</Item>
</Array>
</Set>
</New>
</Set>
<!-- =========================================================== -->
<!-- extra options -->
<!-- =========================================================== -->
<Set name="stopAtShutdown">true</Set>
<Set name="sendServerVersion">true</Set>
<Set name="sendDateHeader">true</Set>
<Set name="gracefulShutdown">1000</Set>
<Set name="dumpAfterStart">false</Set>
<Set name="dumpBeforeStop">false</Set>
<!-- =========================================================== -->
<!-- jetty-jndi by default -->
<!-- =========================================================== -->
<Call class="java.lang.System" name="setProperty">
<Arg>java.naming.factory.initial</Arg>
<Arg><Property name="java.naming.factory.initial" default="org.eclipse.jetty.jndi.InitialContextFactory"/></Arg>
</Call>
<Call class="java.lang.System" name="setProperty">
<Arg>java.naming.factory.url.pkgs</Arg>
<Arg><Property name="java.naming.factory.url.pkgs" default="org.eclipse.jetty.jndi"/></Arg>
</Call>
</Configure>

View File

@ -0,0 +1,177 @@
// ========================================================================
// 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.server.handler.ContextHandler;
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;
import org.osgi.framework.ServiceReference;
/**
* TestJettyOSGiBootContextAsService
*
* Tests deployment of a ContextHandler as an osgi Service.
*
* Tests the ServiceContextProvider.
*
*/
@RunWith( JUnit4TestRunner.class )
public class JettyOSGiBootContextAsService
{
private static final boolean LOGGING_ENABLED = false;
private static final boolean REMOTE_DEBUGGING = false;
@Inject
BundleContext bundleContext = null;
@Configuration
public static Option[] configure()
{
ArrayList<Option> options = new ArrayList<Option>();
options.addAll(TestJettyOSGiBootCore.provisionCoreJetty());
File base = MavenTestingUtils.getBasedir();
File src = new File (base, "src");
File tst = new File (src, "test");
File config = new File (tst, "config");
// Enable Logging
if(LOGGING_ENABLED) {
options.addAll(Arrays.asList(options(
// install log service using pax runners profile abstraction (there are more profiles, like DS)
// logProfile(),
// this is how you set the default log level when using pax logging (logProfile)
systemProperty( "org.ops4j.pax.logging.DefaultServiceLog.level" ).value( "INFO" )
)));
}
// Remote JDWP Debugging
if(REMOTE_DEBUGGING) {
options.addAll(Arrays.asList(options(
// this just adds all what you write here to java vm argumenents of the (new) osgi process.
PaxRunnerOptions.vmOption( "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5006" )
)));
}
// Standard Options
options.addAll(Arrays.asList(options(
PaxRunnerOptions.vmOption("-Djetty.port=9876 -Djetty.home="+config.getAbsolutePath()+" -D" + OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS +
"=etc/jetty.xml;etc/jetty-deployer.xml;etc/jetty-selector.xml;etc/jetty-testrealm.xml"),
/* orbit deps */
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet.jsp" ).versionAsInProject(),
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet.jsp.jstl" ).versionAsInProject(),
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.el" ).versionAsInProject(),
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "com.sun.el" ).versionAsInProject(),
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.apache.jasper.glassfish" ).versionAsInProject(),
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.apache.taglibs.standard.glassfish" ).versionAsInProject(),
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.eclipse.jdt.core" ).versionAsInProject(),
/* jetty-osgi deps */
mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot" ).versionAsInProject().start(),
mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot-jsp" ).versionAsInProject().start(),
//a bundle that registers a webapp as a service for the jetty osgi core to pick up and deploy
mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "test-jetty-osgi-context" ).versionAsInProject().start()
// mavenBundle().groupId( "org.eclipse.equinox.http" ).artifactId( "servlet" ).versionAsInProject().start()
)));
return options.toArray(new Option[options.size()]);
}
/**
* You will get a list of bundles installed by default
* plus your testcase, wrapped into a bundle called pax-exam-probe
*/
@Test
public void listBundles() throws Exception
{
Map<String,Bundle> bundlesIndexedBySymbolicName = new HashMap<String, Bundle>();
for( Bundle b : bundleContext.getBundles() )
{
bundlesIndexedBySymbolicName.put(b.getSymbolicName(), b);
System.err.println("Got " + b.getSymbolicName() + " " + b.getVersion().toString() + " " + b.getState());
}
Bundle osgiBoot = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.osgi.boot");
Assert.assertNotNull("Could not find the org.eclipse.jetty.osgi.boot bundle", osgiBoot);
Assert.assertTrue(osgiBoot.getState() == Bundle.ACTIVE);
Bundle testWebBundle = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.osgi.testcontext");
Assert.assertNotNull("Could not find the org.eclipse.jetty.test-jetty-osgi-context.jar bundle", testWebBundle);
Assert.assertTrue("The bundle org.eclipse.jetty.testcontext is not correctly resolved", testWebBundle.getState() == Bundle.ACTIVE);
//now test the context
HttpClient client = new HttpClient();
client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
try
{
client.start();
ContentExchange getExchange = new ContentExchange();
getExchange.setURL("http://127.0.0.1:9876/acme/index.html");
getExchange.setMethod(HttpMethods.GET);
client.send(getExchange);
int state = getExchange.waitForDone();
Assert.assertEquals("state should be done", HttpExchange.STATUS_COMPLETED, state);
String content = null;
int responseStatus = getExchange.getResponseStatus();
Assert.assertEquals(HttpStatus.OK_200, responseStatus);
if (responseStatus == HttpStatus.OK_200) {
content = getExchange.getResponseContent();
}
Assert.assertTrue(content.indexOf("<h1>Test OSGi Context</h1>") != -1);
}
finally
{
client.stop();
}
ServiceReference[] refs = bundleContext.getServiceReferences(ContextHandler.class.getName(), null);
Assert.assertNotNull(refs);
Assert.assertEquals(1,refs.length);
ContextHandler ch = (ContextHandler)bundleContext.getService(refs[0]);
Assert.assertEquals("/acme", ch.getContextPath());
}
}

View File

@ -16,6 +16,7 @@ package org.eclipse.jetty.osgi.boot;
import static org.ops4j.pax.exam.CoreOptions.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@ -36,6 +37,8 @@ import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Inject;
@ -50,6 +53,13 @@ import org.osgi.service.http.HttpService;
/**
* TestJettyOSGiBootCore
*
* Tests deploying a bundle (org.eclipse.jetty.osgi.httpservice) that contains a context xml file
* that starts up the equinox http servlet.
*
* This tests the BundleContextProvider.
*
* 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.
*/
@ -65,10 +75,17 @@ public class TestJettyOSGiBootCore
*/
public static List<Option> provisionCoreJetty()
{
File base = MavenTestingUtils.getBasedir();
File src = new File (base, "src");
File tst = new File (src, "test");
File config = new File (tst, "config");
return Arrays.asList(options(
// get the jetty home config from the osgi boot bundle.
PaxRunnerOptions.vmOptions("-Djetty.port=9876 -D" + DefaultJettyAtJettyHomeHelper.SYS_PROP_JETTY_HOME_BUNDLE + "=org.eclipse.jetty.osgi.boot"),
// PaxRunnerOptions.vmOptions("-Djetty.port=9876 -D" + DefaultJettyAtJettyHomeHelper.SYS_PROP_JETTY_HOME_BUNDLE + "=org.eclipse.jetty.osgi.boot"),
PaxRunnerOptions.vmOptions("-Djetty.port=9876 -Djetty.home=" + config.getAbsolutePath()),
// CoreOptions.equinox(),
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet" ).versionAsInProject().noStart(),
@ -132,7 +149,6 @@ public class TestJettyOSGiBootCore
for( Bundle b : bundleContext.getBundles() )
{
bundlesIndexedBySymbolicName.put(b.getSymbolicName(), b);
System.err.println("got " + b.getSymbolicName());
}
Bundle osgiBoot = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.osgi.boot");
Assert.assertNotNull("Could not find the org.eclipse.jetty.osgi.boot bundle", osgiBoot);
@ -190,6 +206,9 @@ public class TestJettyOSGiBootCore
{
client.stop();
}
ServiceReference[] refs = bundleContext.getServiceReferences(ContextHandler.class.getName(), null);
Assert.assertNotNull(refs);
Assert.assertEquals(1,refs.length);
}
}

View File

@ -0,0 +1,180 @@
// ========================================================================
// 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.server.handler.ContextHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.webapp.WebAppContext;
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;
import org.osgi.framework.ServiceReference;
/**
* TestJettyOSGiBootWebAppAsService
*
* Tests deployment of a WebAppContext as an osgi Service.
*
* Tests the ServiceWebAppProvider.
*
* 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<Option> options = new ArrayList<Option>();
options.addAll(TestJettyOSGiBootCore.provisionCoreJetty());
File base = MavenTestingUtils.getBasedir();
File src = new File (base, "src");
File tst = new File (src, "test");
File config = new File (tst, "config");
// Enable Logging
if(LOGGING_ENABLED) {
options.addAll(Arrays.asList(options(
// install log service using pax runners profile abstraction (there are more profiles, like DS)
// logProfile(),
// this is how you set the default log level when using pax logging (logProfile)
systemProperty( "org.ops4j.pax.logging.DefaultServiceLog.level" ).value( "INFO" )
)));
}
// Remote JDWP Debugging
if(REMOTE_DEBUGGING) {
options.addAll(Arrays.asList(options(
// this just adds all what you write here to java vm argumenents of the (new) osgi process.
PaxRunnerOptions.vmOption( "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5006" )
)));
}
// Standard Options
options.addAll(Arrays.asList(options(
PaxRunnerOptions.vmOption("-Djetty.port=9876 -Djetty.home="+config.getAbsolutePath()+" -D" + OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS +
"=etc/jetty.xml;etc/jetty-deployer.xml;etc/jetty-selector.xml;etc/jetty-testrealm.xml"),
/* orbit deps */
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet.jsp" ).versionAsInProject(),
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet.jsp.jstl" ).versionAsInProject(),
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.el" ).versionAsInProject(),
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "com.sun.el" ).versionAsInProject(),
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.apache.jasper.glassfish" ).versionAsInProject(),
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.apache.taglibs.standard.glassfish" ).versionAsInProject(),
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "org.eclipse.jdt.core" ).versionAsInProject(),
/* jetty-osgi deps */
mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot" ).versionAsInProject().start(),
mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "jetty-osgi-boot-jsp" ).versionAsInProject().start(),
//a bundle that registers a webapp as a service for the jetty osgi core to pick up and deploy
mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "test-jetty-osgi-webapp" ).versionAsInProject().start()
// mavenBundle().groupId( "org.eclipse.equinox.http" ).artifactId( "servlet" ).versionAsInProject().start()
)));
return options.toArray(new Option[options.size()]);
}
/**
* You will get a list of bundles installed by default
* plus your testcase, wrapped into a bundle called pax-exam-probe
*/
@Test
public void listBundles() throws Exception
{
Map<String,Bundle> bundlesIndexedBySymbolicName = new HashMap<String, Bundle>();
for( Bundle b : bundleContext.getBundles() )
{
bundlesIndexedBySymbolicName.put(b.getSymbolicName(), b);
System.err.println("Got " + b.getSymbolicName() + " " + b.getVersion().toString() + " " + b.getState());
}
Bundle osgiBoot = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.osgi.boot");
Assert.assertNotNull("Could not find the org.eclipse.jetty.osgi.boot bundle", osgiBoot);
Assert.assertTrue(osgiBoot.getState() == Bundle.ACTIVE);
Bundle testWebBundle = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.osgi.testapp");
Assert.assertNotNull("Could not find the org.eclipse.jetty.test-jetty-osgi-webapp.jar bundle", testWebBundle);
Assert.assertTrue("The bundle org.eclipse.jetty.testapp is not correctly resolved", testWebBundle.getState() == Bundle.ACTIVE);
//now test the jsp/dump.jsp
HttpClient client = new HttpClient();
client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
try
{
client.start();
ContentExchange getExchange = new ContentExchange();
getExchange.setURL("http://127.0.0.1:9876/acme/index.html");
getExchange.setMethod(HttpMethods.GET);
client.send(getExchange);
int state = getExchange.waitForDone();
Assert.assertEquals("state should be done", HttpExchange.STATUS_COMPLETED, state);
String content = null;
int responseStatus = getExchange.getResponseStatus();
Assert.assertEquals(HttpStatus.OK_200, responseStatus);
if (responseStatus == HttpStatus.OK_200) {
content = getExchange.getResponseContent();
}
Assert.assertTrue(content.indexOf("<h1>Test OSGi WebApp</h1>") != -1);
}
finally
{
client.stop();
}
ServiceReference[] refs = bundleContext.getServiceReferences(ContextHandler.class.getName(), null);
Assert.assertNotNull(refs);
Assert.assertEquals(1,refs.length);
WebAppContext wac = (WebAppContext)bundleContext.getService(refs[0]);
Assert.assertEquals("/acme", wac.getContextPath());
}
}

View File

@ -29,6 +29,11 @@ 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.server.handler.ContextHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -39,8 +44,15 @@ import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
/**
* TestJettyOSGiBootWithJsp
*
*
* Tests deploying a war (standard jetty test webapp). Tests the BundleWebAppProvider.
*
*
* 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.
*/
@ -57,7 +69,13 @@ public class TestJettyOSGiBootWithJsp
public static Option[] configure()
{
File testrealm = new File("src/test/config/etc/jetty-testrealm.xml");
File base = MavenTestingUtils.getBasedir();
File src = new File (base, "src");
File tst = new File (src, "test");
File config = new File (tst, "config");
ArrayList<Option> options = new ArrayList<Option>();
options.addAll(TestJettyOSGiBootCore.provisionCoreJetty());
@ -81,8 +99,8 @@ public class TestJettyOSGiBootWithJsp
// Standard Options
options.addAll(Arrays.asList(options(
PaxRunnerOptions.vmOption("-Djetty.port=9876 -D" + OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS +
"=etc/jetty.xml;" + testrealm.getAbsolutePath()),
PaxRunnerOptions.vmOption("-Djetty.port=9876 -Djetty.home="+config.getAbsolutePath()+" -D" + OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS +
"=etc/jetty.xml;etc/jetty-deployer.xml;etc/jetty-selector.xml;etc/jetty-testrealm.xml"),
/* orbit deps */
mavenBundle().groupId( "org.eclipse.jetty.orbit" ).artifactId( "javax.servlet.jsp" ).versionAsInProject(),
@ -110,7 +128,7 @@ public class TestJettyOSGiBootWithJsp
* plus your testcase, wrapped into a bundle called pax-exam-probe
*/
@Test
@Ignore
public void listBundles() throws Exception
{
Map<String,Bundle> bundlesIndexedBySymbolicName = new HashMap<String, Bundle>();
@ -153,7 +171,7 @@ public class TestJettyOSGiBootWithJsp
if (responseStatus == HttpStatus.OK_200) {
content = getExchange.getResponseContent();
}
//System.err.println("content: " + content);
Assert.assertTrue(content.indexOf("<tr><th>ServletPath:</th><td>/jsp/dump.jsp</td></tr>") != -1);
}
finally
@ -161,6 +179,11 @@ public class TestJettyOSGiBootWithJsp
client.stop();
}
ServiceReference[] refs = bundleContext.getServiceReferences(ContextHandler.class.getName(), null);
Assert.assertNotNull(refs);
Assert.assertEquals(1,refs.length);
//TODO check that the events got sent
}

View File

@ -378,7 +378,6 @@ public abstract class AbstractHttpConnection extends AbstractConnection
}
}
};
}
_writer.setCharacterEncoding(encoding);
return _printWriter;
@ -402,6 +401,7 @@ public abstract class AbstractHttpConnection extends AbstractConnection
_responseFields.clear();
_response.recycle();
_uri.clear();
_writer=null;
}
/* ------------------------------------------------------------ */

View File

@ -85,7 +85,6 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
protected String _insertSession;
protected String _deleteSession;
protected String _selectSession;
protected String _updateSession;
protected String _updateSessionNode;
protected String _updateSessionAccessTime;
@ -110,6 +109,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
String _dbName;
boolean _isLower;
boolean _isUpper;
public DatabaseAdaptor (DatabaseMetaData dbMeta)
@ -189,6 +189,39 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
return "rowId";
}
public boolean isEmptyStringNull ()
{
return (_dbName.startsWith("oracle"));
}
public PreparedStatement getLoadStatement (Connection connection, String rowId, String contextPath, String virtualHosts)
throws SQLException
{
if (contextPath == null || "".equals(contextPath))
{
if (isEmptyStringNull())
{
PreparedStatement statement = connection.prepareStatement("select * from "+_sessionTable+
" where sessionId = ? and contextPath is null and virtualHost = ?");
statement.setString(1, rowId);
statement.setString(2, virtualHosts);
return statement;
}
}
PreparedStatement statement = connection.prepareStatement("select * from "+_sessionTable+
" where sessionId = ? and contextPath = ? and virtualHost = ?");
statement.setString(1, rowId);
statement.setString(2, contextPath);
statement.setString(3, virtualHosts);
return statement;
}
}
@ -628,10 +661,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
_deleteSession = "delete from "+_sessionTable+
" where "+_sessionTableRowId+" = ?";
_selectSession = "select * from "+_sessionTable+
" where sessionId = ? and contextPath = ? and virtualHost = ?";
_updateSession = "update "+_sessionTable+
" set lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ?, map = ? where "+_sessionTableRowId+" = ?";

View File

@ -24,6 +24,7 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
@ -831,10 +832,7 @@ public class JDBCSessionManager extends AbstractSessionManager
try
{
connection = getConnection();
statement = connection.prepareStatement(_jdbcSessionIdMgr._selectSession);
statement.setString(1, id);
statement.setString(2, canonicalContextPath);
statement.setString(3, vhost);
statement = _jdbcSessionIdMgr._dbAdaptor.getLoadStatement(connection, id, canonicalContextPath, vhost);
ResultSet result = statement.executeQuery();
if (result.next())
{

View File

@ -79,6 +79,15 @@ public class IncludableGzipFilter extends GzipFilter
{
return new GZIPOutputStream(_response.getOutputStream(),_bufferSize);
}
@Override
protected void setHeader(String name, String value)
{
super.setHeader(name, value);
HttpServletResponse response = (HttpServletResponse)getResponse();
if (!response.containsHeader(name))
response.setHeader("org.eclipse.jetty.server.include." + name, value);
}
};
}
};
@ -97,6 +106,15 @@ public class IncludableGzipFilter extends GzipFilter
{
return new DeflaterOutputStream(_response.getOutputStream(),new Deflater(_deflateCompressionLevel, _deflateNoWrap));
}
@Override
protected void setHeader(String name, String value)
{
super.setHeader(name, value);
HttpServletResponse response = (HttpServletResponse)getResponse();
if (!response.containsHeader(name))
response.setHeader("org.eclipse.jetty.server.include." + name, value);
}
};
}
};
@ -108,8 +126,8 @@ public class IncludableGzipFilter extends GzipFilter
configureWrappedResponse(wrappedResponse);
return wrappedResponse;
}
// Extend CompressedResponseWrapper to be able to set headers during include and to create unchecked printwriters
private abstract class IncludableResponseWrapper extends CompressedResponseWrapper
{
@ -124,7 +142,7 @@ public class IncludableGzipFilter extends GzipFilter
super.setHeader(name,value);
HttpServletResponse response = (HttpServletResponse)getResponse();
if (!response.containsHeader(name))
response.setHeader("org.eclipse.jetty.server.include."+name,value);;
response.setHeader("org.eclipse.jetty.server.include."+name,value);
}
@Override
protected PrintWriter newWriter(OutputStream out, String encoding) throws UnsupportedEncodingException