1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 package org.codehaus.ivory.provider;
57
58 import java.lang.reflect.InvocationHandler;
59 import java.lang.reflect.InvocationTargetException;
60 import java.lang.reflect.Method;
61 import java.lang.reflect.Proxy;
62
63 import javax.xml.rpc.server.ServiceLifecycle;
64
65 import org.apache.avalon.framework.service.ServiceManager;
66 import org.apache.axis.AxisFault;
67 import org.apache.axis.MessageContext;
68 import org.apache.axis.handlers.soap.SOAPService;
69 import org.apache.axis.providers.java.RPCProvider;
70
71 /***
72 * Provider class which allows you to specify an Avalon <b>ROLE</b> for
73 * servicing Axis SOAP requests.
74 *
75 * <p>
76 * The specified <b>ROLE</b> corresponds to a particular implementation
77 * which is retrieved by a given Avalon <code>ComponentManager</code>.
78 * For more information about Avalon, see the Avalon.
79 * <a href="http://jakarta.apache.org/avalon">website</a>.
80 * </p>
81 *
82 * <p>
83 * To use this class, you need to add your Avalon <code>ComponentManager</code>
84 * instance to the <code>MessageContext</code> that is Axis uses to process
85 * messages with.
86 * </p>
87 *
88 * <p>
89 * To do this you could for example subclass the AxisServlet and override the
90 * <code>createMessageContext()</code> method adding the ComponentManager, eg:
91 *
92 * <pre>
93 * protected MessageContext createMessageContext(...)
94 * {
95 * MessageContext context = super.createMessageContext();
96 * context.setProperty(AvalonProvider.COMPONENT_MANAGER, m_manager);
97 * return context;
98 * }
99 * </pre>
100 *
101 * and appropriately add the AvalonProvider to the list of handlers in your
102 * server-config.wsdd (suggestions on how to improve this are welcomed)
103 * </p>
104 *
105 * <p>
106 * This provider will use that <code>ComponentManager</code> reference to
107 * retrieve objects.
108 * </p>
109 *
110 * <p>
111 * In your deployment descriptor use the following syntax:
112 *
113 * <pre>
114 * <service name="myservice" provider="java:Avalon">
115 * <parameter name="role" value="my.avalon.role.name"/>
116 * <parameter name="className" value="my.avalon.roles.interface.name"/>
117 * <parameter name="allowedMethods" value="allowed.methods"/>
118 * </service>
119 * </pre>
120 *
121 * </p>
122 *
123 * @author <a href="mailto:crafterm@apache.org">Marcus Crafter</a>
124 * @revision CVS $Id: AvalonProvider.java,v 1.2 2004/08/02 14:14:08 dandiep Exp $
125 */
126 public class AvalonProvider
127 extends RPCProvider
128 {
129 /***
130 * Constant used to retrieve the ServiceManager reference
131 * from the MessageContext object.
132 */
133 public static final String SERVICE_MANAGER = "service-manager";
134
135 /***
136 * Constant which represents the name of the ROLE this
137 * provider should <i>lookup</i> to service a request with. This is
138 * specified in the <parameter name="" value=""/> part of the
139 * deployment xml.
140 */
141 public static final String ROLE = "role";
142
143 /***
144 * Returns the service object.
145 *
146 * @param msgContext the message context
147 * @param role the Avalon ROLE to lookup to find the service object implementation
148 * @return an object that implements the service
149 * @exception Exception if an error occurs
150 */
151 protected Object makeNewServiceObject(
152 MessageContext msgContext,
153 String role)
154 throws Exception
155 {
156 ServiceManager manager =
157 (ServiceManager) msgContext
158 .getAxisEngine()
159 .getApplicationSession()
160 .get( SERVICE_MANAGER );
161
162 if (manager == null)
163 throw new AxisFault("Could not access Avalon ServiceManager");
164
165 return decorate(manager.lookup(role), manager);
166 }
167
168 /***
169 * Helper method for decorating a <code>Component</code> with a Handler
170 * proxy (see below).
171 *
172 * @param object a <code>Component</code> instance
173 * @param manager a <code>ComponentManager</code> instance
174 * @return the <code>Proxy</code> wrapped <code>Component</code> instance
175 * @exception Exception if an error occurs
176 */
177 private Object decorate(final Object object, final ServiceManager manager)
178 throws Exception
179 {
180
181 Class[] interfaces = object.getClass().getInterfaces();
182
183
184 Class[] adjusted = new Class[interfaces.length + 1];
185 System.arraycopy(interfaces, 0, adjusted, 0, interfaces.length);
186 adjusted[interfaces.length] = ServiceLifecycle.class;
187
188
189 Object proxy =
190 Proxy.newProxyInstance(
191 this.getClass().getClassLoader(),
192 adjusted,
193 new Handler(object, manager));
194
195
196 return proxy;
197 }
198
199 /***
200 * Get the service class description
201 *
202 * @param role the Avalon ROLE name
203 * @param service a <code>SOAPService</code> instance
204 * @param msgContext the message context
205 * @return service class description
206 * @exception AxisFault if an error occurs
207 */
208 protected Class getServiceClass(
209 String role,
210 SOAPService service,
211 MessageContext msgContext)
212 throws AxisFault
213 {
214
215
216
217
218 ClassLoader cl = Thread.currentThread().getContextClassLoader();
219 String className = role;
220
221 int i;
222
223 if ((i = role.indexOf('/')) != -1)
224 {
225 className = role.substring(0, i);
226 }
227
228 try
229 {
230 return cl.loadClass( className );
231 }
232 catch (ClassNotFoundException e)
233 {
234 throw new AxisFault( "Couldn't find class " + className, e );
235 }
236 }
237
238 /***
239 * <code>InvocationHandler</code> class for managing Avalon
240 * <code>Components</code>.
241 *
242 * <p>
243 * Components retrieved from an Avalon ComponentManager must be
244 * returned to the manager when they are no longer required.
245 * </p>
246 *
247 * <p>
248 * The returning of Components to their ComponentManager is handled
249 * by a Proxy class which uses the following InvocationHandler.
250 * </p>
251 *
252 * <p>
253 * Each Component returned by this Provider is wrapped inside a
254 * Proxy class which implements all of the Component's interfaces
255 * including javax.xml.rpc.server.ServiceLifecycle.
256 * </p>
257 *
258 * <p>
259 * When Axis is finished with the object returned by this provider,
260 * it invokes ServiceLifecycle.destroy(). This is intercepted by the
261 * InvocationHandler and the Component is returned at this time back
262 * to the ComponentManager it was retrieved from.
263 * </p>
264 *
265 * <p>
266 * <b>Note</b>, when Axis invokes ServiceLifecycle.destroy() is dependant
267 * on the scope of the service (ie. Request, Session & Application).
268 * </p>
269 */
270 final class Handler implements InvocationHandler
271 {
272
273 private final String SL_DESTROY = "destroy";
274 private final Class SL_CLASS = ServiceLifecycle.class;
275
276
277 private final Object m_object;
278 private final ServiceManager m_manager;
279
280 /***
281 * Simple constructor, sets all internal references
282 *
283 * @param object a <code>Component</code> instance
284 * @param manager a <code>ComponentManager</code> instance
285 * @param log a <code>Logger</code> instance
286 */
287 public Handler(final Object object, final ServiceManager manager)
288 {
289 m_object = object;
290 m_manager = manager;
291 }
292
293 /***
294 * <code>invoke</code> method, handles all method invocations for this
295 * particular proxy.
296 *
297 * <p>
298 * Usually the invocation is passed through to the
299 * actual component the proxy wraps, unless the method belongs to
300 * the <code>ServiceLifecycle</code> interface where it is handled
301 * locally.
302 * </p>
303 *
304 * @param proxy the <code>Proxy</code> instance the method was invoked on
305 * @param method the invoked method <code>Method</code> object
306 * @param args an <code>Object[]</code> array of arguments
307 * @return an <code>Object</code> value or null if none
308 * @exception Throwable if an error occurs
309 */
310 public Object invoke(Object proxy, Method method, Object[] args)
311 throws Throwable
312 {
313 try
314 {
315
316 if (method.getDeclaringClass().equals(SL_CLASS))
317 {
318 if (method.getName().equals(SL_DESTROY))
319 {
320 m_manager.release(m_object);
321 }
322
323 return null;
324 }
325 else
326 {
327 return method.invoke(m_object, args);
328 }
329 }
330 catch ( InvocationTargetException e )
331 {
332 throw e.getTargetException();
333 }
334 }
335 }
336 }