View Javadoc

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