bug 341736 Extend the NestedConnector to hook cleanly into the equinox BridgeServlet

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@3045 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Hugues Malphettes 2011-04-30 05:52:04 +00:00
parent 26751210e4
commit 70cd112ab1
8 changed files with 429 additions and 25 deletions

View File

@ -26,7 +26,7 @@ Import-Package: javax.mail;version="1.4.0";resolution:=optional,
org.xml.sax,
org.xml.sax.helpers
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Bundle-Classpath: .
Bundle-Classpath: .,target/classes
Require-Bundle: org.eclipse.jetty.ajp;bundle-version="[7.0,8)";resolution:=optional,
org.eclipse.jetty.annotations;bundle-version="[7.0,8)";resolution:=optional,
org.eclipse.jetty.client;bundle-version="[7.0,8)";resolution:=optional,
@ -36,6 +36,7 @@ Require-Bundle: org.eclipse.jetty.ajp;bundle-version="[7.0,8)";resolution:=optio
org.eclipse.jetty.io;bundle-version="[7.0,8)",
org.eclipse.jetty.jmx;bundle-version="[7.0,8)";resolution:=optional,
org.eclipse.jetty.jndi;bundle-version="[7.0,8)";resolution:=optional,
org.eclipse.jetty.nested;bundle-version="[7.0,8)";resolution:=optional,
org.eclipse.jetty.plus;bundle-version="[7.0,8.0)";resolution:=optional,
org.eclipse.jetty.rewrite;bundle-version="[7.0,8)";resolution:=optional,
org.eclipse.jetty.security;bundle-version="[7.0,8)";resolution:=optional,
@ -47,4 +48,5 @@ Require-Bundle: org.eclipse.jetty.ajp;bundle-version="[7.0,8)";resolution:=optio
org.eclipse.jetty.websocket;bundle-version="[7.0,8)";resolution:=optional,
org.eclipse.jetty.xml;bundle-version="[7.0,8)"
Export-Package: org.eclipse.jetty.osgi.boot;version="7.4.0",
org.eclipse.jetty.osgi.nested;version="7.4.0",
org.eclipse.jetty.osgi.boot.utils;version="7.4.0"

View File

@ -0,0 +1,103 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<!-- =============================================================== -->
<!-- Configure the Jetty Server Nested -->
<!-- inside another Servlet Container -->
<!-- -->
<!-- Documentation of this file format can be found at: -->
<!-- http://docs.codehaus.org/display/JETTY/jetty.xml -->
<!-- -->
<!-- =============================================================== -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.osgi.nested.NestedConnector">
<Set name="statsOn">false</Set>
<Set name="confidentialPort">8443</Set>
</New>
</Arg>
</Call>
<!-- =========================================================== -->
<!-- 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>
<!-- =========================================================== -->
<!-- Configure the deployment manager -->
<!-- -->
<!-- Sets up 2 monitored dir app providers that are configured -->
<!-- to behave in a similaraly to the legacy ContextDeployer -->
<!-- and WebAppDeployer from previous versions of Jetty. -->
<!-- =========================================================== -->
<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 is to plug the tld files of the reference implementation of JSF -->
<Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldsbundles"
default="javax.faces.jsf-impl" /></Set>
</New>
</Arg>
</Call>
</New>
</Arg>
</Call>
<!-- =========================================================== -->
<!-- extra options -->
<!-- =========================================================== -->
<Set name="stopAtShutdown">true</Set>
<Set name="sendServerVersion">true</Set>
<Set name="sendDateHeader">true</Set>
<Set name="gracefulShutdown">1000</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

@ -26,6 +26,10 @@
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-nested</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>osgi</artifactId>

View File

@ -18,9 +18,8 @@ import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Hashtable;
import java.util.StringTokenizer;
import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
@ -94,8 +93,10 @@ public class DefaultJettyAtJettyHomeHelper {
Bundle jettyHomeBundle = null;
if (jettyHomeSysProp != null)
{
jettyHomeSysProp = resolvePropertyValue(jettyHomeSysProp);
//bug 329621
if (jettyHomeSysProp.startsWith("\"") && jettyHomeSysProp.endsWith("\"")) {
if (jettyHomeSysProp.startsWith("\"") && jettyHomeSysProp.endsWith("\"")
|| (jettyHomeSysProp.startsWith("'") && jettyHomeSysProp.endsWith("'"))) {
jettyHomeSysProp = jettyHomeSysProp.substring(1, jettyHomeSysProp.length() - 1);
}
if (jettyHomeBundleSysProp != null)
@ -112,6 +113,7 @@ public class DefaultJettyAtJettyHomeHelper {
}
else if (jettyHomeBundleSysProp != null)
{
jettyHomeBundleSysProp = resolvePropertyValue(jettyHomeBundleSysProp);
for (Bundle b : bundleContext.getBundles())
{
if (b.getSymbolicName().equals(jettyHomeBundleSysProp))
@ -150,6 +152,7 @@ public class DefaultJettyAtJettyHomeHelper {
setProperty(properties,SYS_PROP_JETTY_PORT_SSL,System.getProperty(SYS_PROP_JETTY_PORT_SSL));
bundleContext.registerService(Server.class.getName(), server, properties);
// hookNestedConnectorToBridgeServlet(server);
}
catch (Throwable t)
{
@ -197,7 +200,6 @@ public class DefaultJettyAtJettyHomeHelper {
private static String getJettyConfigurationURLs(Bundle configurationBundle)
{
String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES,"etc/jetty.xml");
System.err.println("jettyetc=" + jettyetc);
StringTokenizer tokenizer = new StringTokenizer(jettyetc,";,", false);
StringBuilder res = new StringBuilder();
@ -256,4 +258,49 @@ public class DefaultJettyAtJettyHomeHelper {
}
}
/**
* recursively substitute the ${sysprop} by their actual system property.
* ${sysprop,defaultvalue} will use 'defaultvalue' as the value if no sysprop is defined.
* Not the most efficient code but we are shooting for simplicity and speed of development here.
*
* @param value
* @return
*/
public static String resolvePropertyValue(String value)
{
int ind = value.indexOf("${");
if (ind == -1) {
return value;
}
int ind2 = value.indexOf('}', ind);
if (ind2 == -1) {
return value;
}
String sysprop = value.substring(ind+2, ind2);
String defaultValue = null;
int comma = sysprop.indexOf(',');
if (comma != -1 && comma+1 != sysprop.length())
{
defaultValue = sysprop.substring(comma+1);
defaultValue = resolvePropertyValue(defaultValue);
sysprop = sysprop.substring(0,comma);
}
else
{
defaultValue = "${" + sysprop + "}";
}
String v = System.getProperty(sysprop);
String reminder = value.length() > ind2 + 1 ? value.substring(ind2+1) : "";
reminder = resolvePropertyValue(reminder);
if (v != null)
{
return value.substring(0, ind) + v + reminder;
}
else
{
return value.substring(0, ind) + defaultValue + reminder;
}
}
}

View File

@ -248,26 +248,6 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer {
rfc66ContextPath = rfc66ContextPath.substring(0,lastDot);
}
}
if (rfc66ContextPath.startsWith("${") && rfc66ContextPath.endsWith("}"))
{
//a system property.
String sysProperty = rfc66ContextPath.substring(2, rfc66ContextPath.length()-1);
String[] keyAndDefaultValue = sysProperty.split(",");
String defaultValue = null;
if (keyAndDefaultValue.length == 2)
{
sysProperty = keyAndDefaultValue[0];
defaultValue = keyAndDefaultValue[1];
}
String sysValue = System.getProperty(sysProperty, defaultValue);
if (sysValue == null)
{
throw new IllegalArgumentException("Could not resolve the system property "
+ sysProperty + " defined in the manifest of the bundle "
+ bundle.getSymbolicName());
}
rfc66ContextPath = sysValue;
}
if (!rfc66ContextPath.startsWith("/"))
{
rfc66ContextPath = "/" + rfc66ContextPath;

View File

@ -0,0 +1,214 @@
// ========================================================================
// Copyright (c) 2010-2011 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.osgi.nested;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServlet;
import org.osgi.framework.FrameworkUtil;
/**
* Extension of the NestedConnector that manages registering and unregistering with the
* BridgeServlet.
* All interactions with the BridgeServlet are done via introspection to avoid depending on it directly.
* The BridgeServlet lives in the bootstrap-webapp; not inside equinox.
*/
public class NestedConnector extends org.eclipse.jetty.nested.NestedConnector
{
/** Name of the BridgeServlet class. By default org.eclipse.equinox.servletbridge.BridgeServlet */
private String bridgeServletClassName = "org.eclipse.equinox.servletbridge.BridgeServlet";
/** Name of the static method on the BridgeServlet class to register the
* servlet delegate. By default 'registerServletDelegate' */
private String registerServletDelegateMethodName = "registerServletDelegate";
/** Name of the static method on the BridgeServlet class to register the
* servlet delegate. By default 'unregisterServletDelegate' */
private String unregisterServletDelegateMethodName = "unregisterServletDelegate";
/** servlet that wraps this NestedConnector and uses the NestedConnector to service the requests. */
private NestedConnectorServletDelegate _servletDelegate;
/**
* @param bridgeServletClassName Name of the class that is the BridgeServlet.
* By default org.eclipse.equinox.servletbridge.BridgeServlet
*/
public void setBridgeServletClassName(String bridgeServletClassName)
{
this.bridgeServletClassName = bridgeServletClassName;
}
public String getBridgeServletClassName()
{
return this.bridgeServletClassName;
}
public String getRegisterServletDelegateMethodName()
{
return this.registerServletDelegateMethodName;
}
public String getUnregisterServletDelegateMethodName()
{
return this.unregisterServletDelegateMethodName;
}
/**
* @param registerServletDelegateMethodName Name of the static method on the BridgeServlet class
* to register the servlet delegate.
*/
public void setRegisterServletDelegateMethodName(String registerServletDelegateMethodName)
{
this.registerServletDelegateMethodName = registerServletDelegateMethodName;
}
/**
* @param unregisterServletDelegateMethodName Name of the static method on the BridgeServlet class
* to unregister the servlet delegate.
*/
public void setUnregisterServletDelegateMethodName(String unregisterServletDelegateMethodName)
{
this.unregisterServletDelegateMethodName = unregisterServletDelegateMethodName;
}
@Override
protected void doStart() throws Exception
{
super.doStart();
registerWithBridgeServlet();
}
@Override
protected void doStop() throws Exception
{
unregisterWithBridgeServlet();
super.doStop();
}
/**
* Hook into the BridgeServlet
*/
protected void registerWithBridgeServlet() throws Exception
{
_servletDelegate = new NestedConnectorServletDelegate(this);
try
{
invokeStaticMethod(getBridgeServletClassName(), getRegisterServletDelegateMethodName(),
new Class[] {HttpServlet.class}, _servletDelegate);
}
catch (Throwable t)
{
_servletDelegate.destroy();
_servletDelegate = null;
if (t instanceof Exception)
{
throw (Exception)t;
}
throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", t);
}
}
/**
* Unhook into the BridgeServlet
*/
protected void unregisterWithBridgeServlet() throws Exception
{
if (_servletDelegate != null)
{
try
{
invokeStaticMethod(getBridgeServletClassName(), getUnregisterServletDelegateMethodName(),
new Class[] {HttpServlet.class}, _servletDelegate);
}
catch (Throwable t)
{
if (t instanceof Exception)
{
throw (Exception)t;
}
throw new RuntimeException("Unable to unregister the servlet delegate from the BridgeServlet.", t);
}
finally
{
_servletDelegate.destroy();
_servletDelegate = null;
}
}
}
/**
*
* @param clName
* @param methName
* @param argType
* @throws Exception
*/
private static void invokeStaticMethod(String clName, String methName, Class[] argType, Object...args)
throws Exception
{
Method m = getMethod(clName, methName, argType);
m.invoke(null, args);
}
/**
*
* @param clName Class that belongs to the parent classloader of the OSGi framework.
* @param methName Name of the method to find.
* @param argType Argument types of the method to find.
* @throws Exception
*/
private static Method getMethod(String clName, String methName, Class... argType)
throws Exception
{
Class bridgeServletClass = FrameworkUtil.class.getClassLoader()
.loadClass(clName);
return getMethod(bridgeServletClass, methName, argType);
}
private static Method getMethod(Class cl, String methName, Class... argType)
throws Exception
{
Method meth = null;
try
{
meth = cl.getMethod(methName, argType);
return meth;
}
catch (Exception e)
{
for (Method m : cl.getMethods())
{
if (m.getName().equals(methName) && m.getParameterTypes().length == argType.length)
{
int i = 0;
for (Class p : m.getParameterTypes())
{
Class ap = argType[i];
if (p.getName().equals(ap.getName())
&& !p.equals(ap))
{
throw new IllegalStateException("The method \"" + m.toGenericString() +
"\" was found. but the parameter class " + p.getName() + " is not the same " +
" inside OSGi classloader (" + ap.getClassLoader() + ") and inside the " +
cl.getName() + " classloader (" + p.getClassLoader() + ")." +
" Are the ExtensionBundles correctly defined?");
}
}
}
}
throw e;
}
}
}

View File

@ -0,0 +1,44 @@
// ========================================================================
// Copyright (c) 2010-2011 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.osgi.nested;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
/**
* Wraps a NestedConnector into a servlet that can be plugged into
* BridgeServlet#registerServletDelegate
*/
public class NestedConnectorServletDelegate extends HttpServlet
{
private static final long serialVersionUID = 1L;
private final NestedConnector _nestedConnector;
public NestedConnectorServletDelegate(NestedConnector nestedConnector)
{
_nestedConnector = nestedConnector;
}
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
_nestedConnector.service(req, res);
}
}

View File

@ -91,6 +91,16 @@
<artifactId>jetty-deploy</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-nested</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>osgi</artifactId>