// ========================================================================
// 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.eclipse.jetty.nested.NestedConnector;
import org.eclipse.jetty.util.component.AbstractLifeCycle.AbstractLifeCycleListener;
import org.eclipse.jetty.util.component.LifeCycle;
import org.osgi.framework.FrameworkUtil;
/**
* Listens to the start and stop of the NestedConnector to register and unregister the NestedConnector
* with the BridgeServlet.
* <p>
* 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.
* </p>
*/
public class NestedConnectorListener extends AbstractLifeCycleListener
{
/** 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;
/**
* The NestedConnector listened to.
*/
private NestedConnector nestedConnector;
/**
* @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;
}
/**
* @param nestedConnector The NestedConnector that we are listening to here.
*/
public void setNestedConnector(NestedConnector nestedConnector)
{
this.nestedConnector = nestedConnector;
}
/**
* @return The NestedConnector that we are listening to here.
*/
public NestedConnector getNestedConnector()
{
return this.nestedConnector;
}
@Override
public void lifeCycleStarted(LifeCycle event)
{
try
{
registerWithBridgeServlet();
}
catch (Exception e)
{
if (e instanceof RuntimeException)
{
throw (RuntimeException)e;
}
throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", e);
}
}
@Override
public void lifeCycleStopping(LifeCycle event)
{
try
{
unregisterWithBridgeServlet();
}
catch (Exception e)
{
if (e instanceof RuntimeException)
{
throw (RuntimeException)e;
}
throw new RuntimeException("Unable to unregister the servlet delegate into the BridgeServlet.", e);
}
}
/**
* Hook into the BridgeServlet
*/
protected void registerWithBridgeServlet() throws Exception
{
_servletDelegate = new NestedConnectorServletDelegate(getNestedConnector());
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;
}
}
}