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
26   * unregister the NestedConnector with the BridgeServlet.
27   * <p>
28   * All interactions with the BridgeServlet are done via introspection to avoid
29   * depending on it directly. The BridgeServlet lives in the bootstrap-webapp;
30   * not inside equinox.
31   * </p>
32   */
33  public class NestedConnectorListener extends AbstractLifeCycleListener
34  {
35  
36      /**
37       * Name of the BridgeServlet class. By default
38       * org.eclipse.equinox.servletbridge.BridgeServlet
39       */
40      private String bridgeServletClassName = "org.eclipse.equinox.servletbridge.BridgeServlet";
41  
42      /**
43       * Name of the static method on the BridgeServlet class to register the
44       * servlet delegate. By default 'registerServletDelegate'
45       */
46      private String registerServletDelegateMethodName = "registerServletDelegate";
47  
48      /**
49       * Name of the static method on the BridgeServlet class to register the
50       * servlet delegate. By default 'unregisterServletDelegate'
51       */
52      private String unregisterServletDelegateMethodName = "unregisterServletDelegate";
53  
54      /**
55       * servlet that wraps this NestedConnector and uses the NestedConnector to
56       * service the requests.
57       */
58      private NestedConnectorServletDelegate _servletDelegate;
59  
60      /**
61       * The NestedConnector listened to.
62       */
63      private NestedConnector nestedConnector;
64  
65      /**
66       * @param bridgeServletClassName Name of the class that is the
67       *            BridgeServlet. By default
68       *            org.eclipse.equinox.servletbridge.BridgeServlet
69       */
70      public void setBridgeServletClassName(String bridgeServletClassName)
71      {
72          this.bridgeServletClassName = bridgeServletClassName;
73      }
74  
75      public String getBridgeServletClassName()
76      {
77          return this.bridgeServletClassName;
78      }
79  
80      public String getRegisterServletDelegateMethodName()
81      {
82          return this.registerServletDelegateMethodName;
83      }
84  
85      public String getUnregisterServletDelegateMethodName()
86      {
87          return this.unregisterServletDelegateMethodName;
88      }
89  
90      /**
91       * @param registerServletDelegateMethodName Name of the static method on the
92       *            BridgeServlet class to register the servlet delegate.
93       */
94      public void setRegisterServletDelegateMethodName(String registerServletDelegateMethodName)
95      {
96          this.registerServletDelegateMethodName = registerServletDelegateMethodName;
97      }
98  
99      /**
100      * @param unregisterServletDelegateMethodName Name of the static method on
101      *            the BridgeServlet class to unregister the servlet delegate.
102      */
103     public void setUnregisterServletDelegateMethodName(String unregisterServletDelegateMethodName)
104     {
105         this.unregisterServletDelegateMethodName = unregisterServletDelegateMethodName;
106     }
107 
108     /**
109      * @param nestedConnector The NestedConnector that we are listening to here.
110      */
111     public void setNestedConnector(NestedConnector nestedConnector)
112     {
113         this.nestedConnector = nestedConnector;
114     }
115 
116     /**
117      * @return The NestedConnector that we are listening to here.
118      */
119     public NestedConnector getNestedConnector()
120     {
121         return this.nestedConnector;
122     }
123 
124     @Override
125     public void lifeCycleStarted(LifeCycle event)
126     {
127         try
128         {
129             registerWithBridgeServlet();
130         }
131         catch (Exception e)
132         {
133             if (e instanceof RuntimeException) { throw (RuntimeException) e; }
134             throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", e);
135         }
136     }
137 
138     @Override
139     public void lifeCycleStopping(LifeCycle event)
140     {
141         try
142         {
143             unregisterWithBridgeServlet();
144         }
145         catch (Exception e)
146         {
147             if (e instanceof RuntimeException) { throw (RuntimeException) e; }
148             throw new RuntimeException("Unable to unregister the servlet delegate into the BridgeServlet.", e);
149         }
150     }
151 
152     /**
153      * Hook into the BridgeServlet
154      */
155     protected void registerWithBridgeServlet() throws Exception
156     {
157         _servletDelegate = new NestedConnectorServletDelegate(getNestedConnector());
158         try
159         {
160             invokeStaticMethod(getBridgeServletClassName(), getRegisterServletDelegateMethodName(), new Class[] { HttpServlet.class }, _servletDelegate);
161         }
162         catch (Throwable t)
163         {
164             _servletDelegate.destroy();
165             _servletDelegate = null;
166             if (t instanceof Exception) { throw (Exception) t; }
167             throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", t);
168         }
169     }
170 
171     /**
172      * Unhook into the BridgeServlet
173      */
174     protected void unregisterWithBridgeServlet() throws Exception
175     {
176         if (_servletDelegate != null)
177         {
178             try
179             {
180                 invokeStaticMethod(getBridgeServletClassName(), getUnregisterServletDelegateMethodName(), new Class[] { HttpServlet.class }, _servletDelegate);
181             }
182             catch (Throwable t)
183             {
184                 if (t instanceof Exception) { throw (Exception) t; }
185                 throw new RuntimeException("Unable to unregister the servlet delegate from the BridgeServlet.", t);
186             }
187             finally
188             {
189                 _servletDelegate.destroy();
190                 _servletDelegate = null;
191             }
192         }
193     }
194 
195     /**
196      * 
197      * @param clName
198      * @param methName
199      * @param argType
200      * @throws Exception
201      */
202     private static void invokeStaticMethod(String clName, String methName, Class[] argType, Object... args) 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
211      *            framework.
212      * @param methName Name of the method to find.
213      * @param argType Argument types of the method to find.
214      * @throws Exception
215      */
216     private static Method getMethod(String clName, String methName, Class... argType) throws Exception
217     {
218         Class bridgeServletClass = FrameworkUtil.class.getClassLoader().loadClass(clName);
219         return getMethod(bridgeServletClass, methName, argType);
220     }
221 
222     private static Method getMethod(Class cl, String methName, Class... argType) throws Exception
223     {
224         Method meth = null;
225         try
226         {
227             meth = cl.getMethod(methName, argType);
228             return meth;
229         }
230         catch (Exception e)
231         {
232             for (Method m : cl.getMethods())
233             {
234                 if (m.getName().equals(methName) && m.getParameterTypes().length == argType.length)
235                 {
236                     int i = 0;
237                     for (Class p : m.getParameterTypes())
238                     {
239                         Class ap = argType[i];
240                         if (p.getName().equals(ap.getName()) && !p.equals(ap)) 
241                         { 
242                             throw new IllegalStateException("The method \"" + m.toGenericString()
243                                                             + "\" was found. but the parameter class "
244                                                             + p.getName()
245                                                             + " is not the same "
246                                                             + " inside OSGi classloader ("
247                                                             + ap.getClassLoader()
248                                                             + ") and inside the "
249                                                             + cl.getName()
250                                                             + " classloader ("
251                                                             + p.getClassLoader()
252                                                             + ")."
253                                                             + " Are the ExtensionBundles correctly defined?");
254 
255                         }
256                     }
257                 }
258             }
259             throw e;
260         }
261     }
262 }