View Javadoc

1   package org.codehaus.ivory.provider;
2   
3   import java.lang.reflect.InvocationHandler;
4   import java.lang.reflect.InvocationTargetException;
5   import java.lang.reflect.Method;
6   import java.lang.reflect.Proxy;
7   
8   import javax.xml.rpc.server.ServiceLifecycle;
9   
10  import org.apache.avalon.framework.service.ServiceManager;
11  import org.apache.axis.AxisFault;
12  import org.apache.axis.MessageContext;
13  import org.apache.axis.handlers.soap.SOAPService;
14  
15  /***
16   * 
17   * @author <a href="mailto:dan@envoisolutions.com">Dan Diephouse</a>
18   * @since May 23, 2003
19   */
20  public class IvoryAvalonProvider
21      extends IvoryProvider
22  {
23      /***
24       * Constant used to retrieve the ServiceManager reference
25       * from the MessageContext object.
26       */
27      public static final String SERVICE_MANAGER = "service-manager";
28  
29      /***
30       * Constant which represents the name of the ROLE this
31       * provider should <i>lookup</i> to service a request with. This is
32       * specified in the &lt;parameter name="" value=""/&gt; part of the
33       * deployment xml.
34       */
35      public static final String ROLE = "role";
36  
37      /***
38       * Returns the service object.
39       * 
40       * @param msgContext the message context
41       * @param role the Avalon ROLE to lookup to find the service object implementation
42       * @return an object that implements the service
43       * @exception Exception if an error occurs
44       */
45      protected Object makeNewServiceObject(
46          MessageContext msgContext,
47          String role)
48          throws Exception
49      {
50          ServiceManager manager =
51              (ServiceManager) msgContext
52                  .getAxisEngine()
53                  .getApplicationSession()
54                  .get( SERVICE_MANAGER );
55  
56          if (manager == null)
57              throw new AxisFault("Could not access Avalon ServiceManager");
58  
59          return decorate(manager.lookup(role), manager);
60      }
61  
62      /***
63       * Helper method for decorating a <code>Component</code> with a Handler
64       * proxy (see below).
65       *
66       * @param object a <code>Component</code> instance
67       * @param manager a <code>ComponentManager</code> instance
68       * @return the <code>Proxy</code> wrapped <code>Component</code> instance
69       * @exception Exception if an error occurs
70       */
71      private Object decorate(final Object object, final ServiceManager manager)
72          throws Exception
73      {
74          // obtain a list of all interfaces this object implements
75          Class[] interfaces = object.getClass().getInterfaces();
76  
77          // add ServiceLifecycle to it
78          Class[] adjusted = new Class[interfaces.length + 1];
79          System.arraycopy(interfaces, 0, adjusted, 0, interfaces.length);
80          adjusted[interfaces.length] = ServiceLifecycle.class;
81  
82          // create a proxy implementing those interfaces
83          Object proxy =
84              Proxy.newProxyInstance(
85                  this.getClass().getClassLoader(),
86                  adjusted,
87                  new Handler(object, manager));
88  
89          // return the proxy
90          return proxy;
91      }
92  
93      /***
94       * Get the service class description
95       * 
96       * @param role the Avalon ROLE name
97       * @param service a <code>SOAPService</code> instance
98       * @param msgContext the message context
99       * @return service class description
100      * @exception AxisFault if an error occurs
101      */
102     protected Class getServiceClass(
103         String role,
104         SOAPService service,
105         MessageContext msgContext)
106         throws AxisFault
107     {
108         // Assuming ExcaliburComponentManager semantics the ROLE name is
109         // actually the class name, potentially with a variant following
110         // the class name with a '/' separator
111 
112         int i;
113 
114         ClassLoader cl = Thread.currentThread().getContextClassLoader();
115         String className = role;
116         
117         if ((i = role.indexOf('/')) != -1)
118         {
119             className = role.substring(0, i);
120         }
121         
122         try
123         {
124             return cl.loadClass( className );
125         }
126         catch (ClassNotFoundException e)
127         {
128             throw new AxisFault( "Couldn't find class " + className, e );
129         }
130     }
131 
132     /***
133      * <code>InvocationHandler</code> class for managing Avalon
134      * <code>Components</code>.
135      *
136      * <p>
137      *  Components retrieved from an Avalon ComponentManager must be
138      *  returned to the manager when they are no longer required.
139      * </p>
140      *
141      * <p>
142      *  The returning of Components to their ComponentManager is handled
143      *  by a Proxy class which uses the following InvocationHandler.
144      * </p>
145      *
146      * <p>
147      *  Each Component returned by this Provider is wrapped inside a 
148      *  Proxy class which implements all of the Component's interfaces
149      *  including javax.xml.rpc.server.ServiceLifecycle.
150      * </p>
151      *
152      * <p>
153      *  When Axis is finished with the object returned by this provider,
154      *  it invokes ServiceLifecycle.destroy(). This is intercepted by the
155      *  InvocationHandler and the Component is returned at this time back
156      *  to the ComponentManager it was retrieved from.
157      * </p>
158      *
159      * <p>
160      *  <b>Note</b>, when Axis invokes ServiceLifecycle.destroy() is dependant
161      *  on the scope of the service (ie. Request, Session & Application).
162      * </p>
163      */
164     final class Handler implements InvocationHandler
165     {
166         // Constants describing the ServiceLifecycle.destroy method
167         private final String SL_DESTROY = "destroy";
168         private final Class SL_CLASS = ServiceLifecycle.class;
169 
170         // Component & ServiceManager references
171         private final Object m_object;
172         private final ServiceManager m_manager;
173 
174         /***
175          * Simple constructor, sets all internal references
176          *
177          * @param object a <code>Component</code> instance
178          * @param manager a <code>ComponentManager</code> instance
179          * @param log a <code>Logger</code> instance
180          */
181         public Handler(final Object object, final ServiceManager manager)
182         {
183             m_object = object;
184             m_manager = manager;
185         }
186 
187         /***
188          * <code>invoke</code> method, handles all method invocations for this
189          * particular proxy.
190          *
191          * <p>
192          *  Usually the invocation is passed through to the
193          *  actual component the proxy wraps, unless the method belongs to
194          *  the <code>ServiceLifecycle</code> interface where it is handled
195          *  locally.
196          * </p>
197          *
198          * @param proxy the <code>Proxy</code> instance the method was invoked on
199          * @param method the invoked method <code>Method</code> object
200          * @param args an <code>Object[]</code> array of arguments
201          * @return an <code>Object</code> value or null if none
202          * @exception Throwable if an error occurs
203          */
204         public Object invoke(Object proxy, Method method, Object[] args)
205             throws Throwable
206         {
207             try
208             {
209                 // if ServiceLifecycle.destroy() called, return to CM
210                 if (method.getDeclaringClass().equals(SL_CLASS))
211                 {
212                     if (method.getName().equals(SL_DESTROY))
213                     {
214                         m_manager.release(m_object);
215                     }
216     
217                     return null;
218                 }
219                 else // otherwise pass call to the real object
220                     {
221                     return method.invoke(m_object, args);
222                 }
223             }
224             catch ( InvocationTargetException e )
225             {
226                 throw e.getTargetException();
227             }
228         }
229     }
230 }