View Javadoc

1   // ========================================================================
2   // Copyright (c) 2010-2011 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  package org.eclipse.jetty.osgi.nested;
14  
15  import java.lang.reflect.Method;
16  
17  import javax.servlet.http.HttpServlet;
18  
19  import org.eclipse.jetty.nested.NestedConnector;
20  import org.eclipse.jetty.util.component.AbstractLifeCycle.AbstractLifeCycleListener;
21  import org.eclipse.jetty.util.component.LifeCycle;
22  import org.osgi.framework.FrameworkUtil;
23  
24  /**
25   * Listens to the start and stop of the NestedConnector to register and unregister the NestedConnector
26   * with the BridgeServlet.
27   * <p>
28   * All interactions with the BridgeServlet are done via introspection to avoid depending on it directly.
29   * The BridgeServlet lives in the bootstrap-webapp; not inside equinox.
30   * </p>
31   */
32  public class NestedConnectorListener extends AbstractLifeCycleListener
33  {
34  
35  	/** Name of the BridgeServlet class. By default org.eclipse.equinox.servletbridge.BridgeServlet */
36  	private String bridgeServletClassName = "org.eclipse.equinox.servletbridge.BridgeServlet";
37  	
38  	/** Name of the static method on the BridgeServlet class to register the
39  	 * servlet delegate. By default 'registerServletDelegate' */
40  	private String registerServletDelegateMethodName = "registerServletDelegate";
41  	
42  	/** Name of the static method on the BridgeServlet class to register the
43  	 * servlet delegate. By default 'unregisterServletDelegate' */
44  	private String unregisterServletDelegateMethodName = "unregisterServletDelegate";
45  	
46  	/** servlet that wraps this NestedConnector and uses the NestedConnector to service the requests. */
47  	private NestedConnectorServletDelegate _servletDelegate;
48  	
49  	/**
50  	 * The NestedConnector listened to.
51  	 */
52  	private NestedConnector nestedConnector;
53  	
54  	/**
55  	 * @param bridgeServletClassName Name of the class that is the BridgeServlet.
56  	 * By default org.eclipse.equinox.servletbridge.BridgeServlet
57  	 */
58  	public void setBridgeServletClassName(String bridgeServletClassName)
59  	{
60  		this.bridgeServletClassName = bridgeServletClassName;
61  	}
62  	
63  	public String getBridgeServletClassName()
64  	{
65  		return this.bridgeServletClassName;
66  	}
67  	public String getRegisterServletDelegateMethodName()
68  	{
69  		return this.registerServletDelegateMethodName;
70  	}
71  	public String getUnregisterServletDelegateMethodName()
72  	{
73  		return this.unregisterServletDelegateMethodName;
74  	}
75  	
76  	/**
77  	 * @param registerServletDelegateMethodName Name of the static method on the BridgeServlet class
78  	 * to register the servlet delegate.
79  	 */
80  	public void setRegisterServletDelegateMethodName(String registerServletDelegateMethodName)
81  	{
82  		this.registerServletDelegateMethodName = registerServletDelegateMethodName;
83  	}
84  	
85  	/**
86  	 * @param unregisterServletDelegateMethodName Name of the static method on the BridgeServlet class
87  	 * to unregister the servlet delegate.
88  	 */
89  	public void setUnregisterServletDelegateMethodName(String unregisterServletDelegateMethodName)
90  	{
91  		this.unregisterServletDelegateMethodName = unregisterServletDelegateMethodName;
92  	}
93  	
94  	/**
95  	 * @param nestedConnector The NestedConnector that we are listening to here.
96  	 */
97  	public void setNestedConnector(NestedConnector nestedConnector)
98  	{
99  		this.nestedConnector = nestedConnector;
100 	}
101 	/**
102 	 * @return The NestedConnector that we are listening to here.
103 	 */
104 	public NestedConnector getNestedConnector()
105 	{
106 		return this.nestedConnector;
107 	}
108 	
109 	@Override
110     public void lifeCycleStarted(LifeCycle event)
111     {
112     	try
113     	{
114 			registerWithBridgeServlet();
115 		}
116     	catch (Exception e)
117     	{
118 			if (e instanceof RuntimeException)
119 			{
120 				throw (RuntimeException)e;
121 			}
122 			throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", e);
123 		}
124     }
125     
126 	@Override
127     public void lifeCycleStopping(LifeCycle event)
128     {
129     	try
130     	{
131     		unregisterWithBridgeServlet();
132 		}
133     	catch (Exception e)
134     	{
135 			if (e instanceof RuntimeException)
136 			{
137 				throw (RuntimeException)e;
138 			}
139 			throw new RuntimeException("Unable to unregister the servlet delegate into the BridgeServlet.", e);
140 		}
141     }
142 
143 	/**
144 	 * Hook into the BridgeServlet
145 	 */
146 	protected void registerWithBridgeServlet() throws Exception
147 	{
148 		_servletDelegate = new NestedConnectorServletDelegate(getNestedConnector());
149 		try
150 		{
151 			invokeStaticMethod(getBridgeServletClassName(), getRegisterServletDelegateMethodName(),
152 				new Class[] {HttpServlet.class}, _servletDelegate);
153 		}
154 		catch (Throwable t)
155 		{
156 			_servletDelegate.destroy();
157 			_servletDelegate = null;
158 			if (t instanceof Exception)
159 			{
160 				throw (Exception)t;
161 			}
162 			throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", t);
163 		}
164 	}
165 	
166 	/**
167 	 * Unhook into the BridgeServlet
168 	 */
169 	protected void unregisterWithBridgeServlet() throws Exception
170 	{
171 		if (_servletDelegate != null)
172 		{
173 			try
174 			{
175 				invokeStaticMethod(getBridgeServletClassName(), getUnregisterServletDelegateMethodName(),
176 					new Class[] {HttpServlet.class}, _servletDelegate);
177 			}
178 			catch (Throwable t)
179 			{
180 				if (t instanceof Exception)
181 				{
182 					throw (Exception)t;
183 				}
184 				throw new RuntimeException("Unable to unregister the servlet delegate from the BridgeServlet.", t);
185 			}
186 			finally 
187 			{
188 				_servletDelegate.destroy();
189 				_servletDelegate = null;				
190 			}
191 		}
192 	}
193 
194 	/**
195 	 * 
196 	 * @param clName
197 	 * @param methName
198 	 * @param argType
199 	 * @throws Exception
200 	 */
201 	private static void invokeStaticMethod(String clName, String methName, Class[] argType, Object...args)
202 	throws Exception
203 	{
204 		Method m = getMethod(clName, methName, argType);
205 		m.invoke(null, args);
206 	}
207 
208 	/**
209 	 * 
210 	 * @param clName Class that belongs to the parent classloader of the OSGi framework.
211 	 * @param methName Name of the method to find.
212 	 * @param argType Argument types of the method to find.
213 	 * @throws Exception
214 	 */
215 	private static Method getMethod(String clName, String methName, Class... argType)
216 	throws Exception 
217 	{
218 		Class bridgeServletClass = FrameworkUtil.class.getClassLoader()
219 				.loadClass(clName);
220 		return getMethod(bridgeServletClass, methName, argType);
221 	}
222 	private static Method getMethod(Class cl, String methName, Class... argType)
223 	throws Exception 
224 	{
225 		Method meth = null;
226 		try
227 		{
228 			meth = cl.getMethod(methName, argType);
229 			return meth;
230 		}
231 		catch (Exception e)
232 		{
233 			for (Method m : cl.getMethods())
234 			{
235 				if (m.getName().equals(methName) && m.getParameterTypes().length == argType.length)
236 				{
237 					int i = 0;
238 					for (Class p : m.getParameterTypes())
239 					{
240 						Class ap = argType[i];
241 						if (p.getName().equals(ap.getName())
242 								&& !p.equals(ap))
243 						{
244 							throw new IllegalStateException("The method \"" + m.toGenericString() +
245 									"\" was found. but the parameter class " + p.getName() + " is not the same " +
246 									" inside OSGi classloader (" + ap.getClassLoader() + ") and inside the " +
247 									cl.getName() + " classloader (" + p.getClassLoader() + ")." +
248 									" Are the ExtensionBundles correctly defined?");
249 							 
250 						}
251 					}
252 				}
253 			}
254 			throw e;
255 		}
256 	}
257 }