From 70cd112ab1c507887f6d6055b02d2e104d4cfbb8 Mon Sep 17 00:00:00 2001 From: Hugues Malphettes Date: Sat, 30 Apr 2011 05:52:04 +0000 Subject: [PATCH] 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 --- .../jetty-osgi-boot/META-INF/MANIFEST.MF | 4 +- .../etc/jetty-osgi-nested-default.xml | 103 +++++++++ jetty-osgi/jetty-osgi-boot/pom.xml | 4 + .../DefaultJettyAtJettyHomeHelper.java | 55 ++++- .../webapp/WebBundleTrackerCustomizer.java | 20 -- .../jetty/osgi/nested/NestedConnector.java | 214 ++++++++++++++++++ .../NestedConnectorServletDelegate.java | 44 ++++ jetty-osgi/pom.xml | 10 + 8 files changed, 429 insertions(+), 25 deletions(-) create mode 100644 jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-nested-default.xml create mode 100644 jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnector.java create mode 100644 jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnectorServletDelegate.java diff --git a/jetty-osgi/jetty-osgi-boot/META-INF/MANIFEST.MF b/jetty-osgi/jetty-osgi-boot/META-INF/MANIFEST.MF index 792c3657ca6..dbc515fd336 100644 --- a/jetty-osgi/jetty-osgi-boot/META-INF/MANIFEST.MF +++ b/jetty-osgi/jetty-osgi-boot/META-INF/MANIFEST.MF @@ -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" diff --git a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-nested-default.xml b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-nested-default.xml new file mode 100644 index 00000000000..48c8c6338da --- /dev/null +++ b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-nested-default.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + false + 8443 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern + .*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$ + + + + + + + 5 + /contexts + + + + + + + + + + + + + + true + true + true + 1000 + + + + java.naming.factory.initial + + + + java.naming.factory.url.pkgs + + + + diff --git a/jetty-osgi/jetty-osgi-boot/pom.xml b/jetty-osgi/jetty-osgi-boot/pom.xml index fbcd12ee744..489f3b7bf03 100644 --- a/jetty-osgi/jetty-osgi-boot/pom.xml +++ b/jetty-osgi/jetty-osgi-boot/pom.xml @@ -26,6 +26,10 @@ org.eclipse.jetty jetty-jmx + + org.eclipse.jetty + jetty-nested + org.eclipse osgi diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java index b1daf6299c7..3c9878fb169 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java @@ -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; + } + } } diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java index a0fa6aaf759..e5cb24c8ad0 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java @@ -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; diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnector.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnector.java new file mode 100644 index 00000000000..cd672b61d6b --- /dev/null +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnector.java @@ -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; + } + } +} diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnectorServletDelegate.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnectorServletDelegate.java new file mode 100644 index 00000000000..063e3cf6d04 --- /dev/null +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/nested/NestedConnectorServletDelegate.java @@ -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); + } + +} diff --git a/jetty-osgi/pom.xml b/jetty-osgi/pom.xml index c4cd3492869..ec9a803aa4f 100644 --- a/jetty-osgi/pom.xml +++ b/jetty-osgi/pom.xml @@ -91,6 +91,16 @@ jetty-deploy ${project.version} + + org.eclipse.jetty + jetty-nested + ${project.version} + + + org.eclipse.jetty + jetty-servlet + ${project.version} + org.eclipse osgi