001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: // package org.apache.axis.providers.java;
018: // currently part of Cocoon until it's officially in Axis CVS (BZ#12903)
019: package org.apache.cocoon.components.axis.providers;
020:
021: import org.apache.avalon.framework.component.Component;
022: import org.apache.avalon.framework.component.ComponentManager;
023:
024: import org.apache.axis.AxisFault;
025: import org.apache.axis.MessageContext;
026: import org.apache.axis.providers.java.RPCProvider;
027: import org.apache.axis.handlers.soap.SOAPService;
028:
029: import java.lang.reflect.Method;
030: import java.lang.reflect.InvocationHandler;
031: import java.lang.reflect.Proxy;
032:
033: import javax.xml.rpc.server.ServiceLifecycle;
034:
035: /**
036: * Provider class which allows you to specify an Avalon <b>ROLE</b> for
037: * servicing Axis SOAP requests.
038: *
039: * <p>
040: * The specified <b>ROLE</b> corresponds to a particular implementation
041: * which is retrieved by a given Avalon <code>ComponentManager</code>.
042: * For more information about Avalon, see the Avalon.
043: * <a href="http://jakarta.apache.org/avalon">website</a>.
044: * </p>
045: *
046: * <p>
047: * To use this class, you need to add your Avalon <code>ComponentManager</code>
048: * instance to the <code>MessageContext</code> that is Axis uses to process
049: * messages with.
050: * </p>
051: *
052: * <p>
053: * To do this you could for example subclass the AxisServlet and override the
054: * <code>createMessageContext()</code> method adding the ComponentManager, eg:
055: *
056: * <pre>
057: * protected MessageContext createMessageContext(...)
058: * {
059: * MessageContext context = super.createMessageContext();
060: * context.setProperty(AvalonProvider.COMPONENT_MANAGER, m_manager);
061: * return context;
062: * }
063: * </pre>
064: *
065: * and appropriately add the AvalonProvider to the list of handlers in your
066: * server-config.wsdd (suggestions on how to improve this are welcomed)
067: * </p>
068: *
069: * <p>
070: * This provider will use that <code>ComponentManager</code> reference to
071: * retrieve objects.
072: * </p>
073: *
074: * <p>
075: * In your deployment descriptor use the following syntax:
076: *
077: * <pre>
078: * <service name="myservice" provider="java:Avalon">
079: * <parameter name="role" value="my.avalon.role.name"/>
080: * <parameter name="className" value="my.avalon.roles.interface.name"/>
081: * <parameter name="allowedMethods" value="allowed.methods"/>
082: * </service>
083: * </pre>
084: *
085: * </p>
086: *
087: * @author <a href="mailto:crafterm@apache.org">Marcus Crafter</a>
088: * @version CVS $Id: AvalonProvider.java 433543 2006-08-22 06:22:54Z crossley $
089: */
090: public class AvalonProvider extends RPCProvider {
091: /**
092: * Constant used to retrieve the ComponentManager reference
093: * from the MessageContext object.
094: */
095: public static final String COMPONENT_MANAGER = "component-manager";
096:
097: /**
098: * Constant which represents the name of the ROLE this
099: * provider should <i>lookup</i> to service a request with. This is
100: * specified in the <parameter name="" value=""/> part of the
101: * deployment xml.
102: */
103: public static final String ROLE = "role";
104:
105: /**
106: * Returns the service object.
107: *
108: * @param msgContext the message context
109: * @param role the Avalon ROLE to lookup to find the service object implementation
110: * @return an object that implements the service
111: * @exception Exception if an error occurs
112: */
113: protected Object makeNewServiceObject(MessageContext msgContext,
114: String role) throws Exception {
115: ComponentManager manager = (ComponentManager) msgContext
116: .getProperty(COMPONENT_MANAGER);
117:
118: if (manager == null)
119: throw new AxisFault(
120: "Could not access Avalon ComponentManager");
121:
122: return decorate(manager.lookup(role), manager);
123: }
124:
125: /**
126: * Helper method for decorating a <code>Component</code> with a Handler
127: * proxy (see below).
128: *
129: * @param object a <code>Component</code> instance
130: * @param manager a <code>ComponentManager</code> instance
131: * @return the <code>Proxy</code> wrapped <code>Component</code> instance
132: * @exception Exception if an error occurs
133: */
134: private Object decorate(final Component object,
135: final ComponentManager manager) throws Exception {
136: // obtain a list of all interfaces this object implements
137: Class[] interfaces = object.getClass().getInterfaces();
138:
139: // add ServiceLifecycle to it
140: Class[] adjusted = new Class[interfaces.length + 1];
141: System.arraycopy(interfaces, 0, adjusted, 0, interfaces.length);
142: adjusted[interfaces.length] = ServiceLifecycle.class;
143:
144: // create a proxy implementing those interfaces
145: Object proxy = Proxy.newProxyInstance(this .getClass()
146: .getClassLoader(), adjusted, new Handler(object,
147: manager));
148:
149: // return the proxy
150: return proxy;
151: }
152:
153: /**
154: * Return the option in the configuration that contains the service class
155: * name. In the Avalon case, it is the ROLE name to lookup.
156: */
157: protected String getServiceClassNameOptionName() {
158: return ROLE;
159: }
160:
161: /**
162: * Get the service class description
163: *
164: * @param role the Avalon ROLE name
165: * @param service a <code>SOAPService</code> instance
166: * @param msgContext the message context
167: * @return service class description
168: * @exception AxisFault if an error occurs
169: */
170: protected Class getServiceClass(String role, SOAPService service,
171: MessageContext msgContext) throws AxisFault {
172: // Assuming ExcaliburComponentManager semantics the ROLE name is
173: // actually the class name, potentially with a variant following
174: // the class name with a '/' separator
175:
176: try {
177: int i;
178:
179: if ((i = role.indexOf('/')) != -1) {
180: return Class.forName(role.substring(0, i));
181: }
182: return Class.forName(role);
183: } catch (ClassNotFoundException e) {
184: throw new AxisFault(
185: "Couldn't create class object for role " + role, e);
186: }
187: }
188:
189: /**
190: * <code>InvocationHandler</code> class for managing Avalon
191: * <code>Components</code>.
192: *
193: * <p>
194: * Components retrieved from an Avalon ComponentManager must be
195: * returned to the manager when they are no longer required.
196: * </p>
197: *
198: * <p>
199: * The returning of Components to their ComponentManager is handled
200: * by a Proxy class which uses the following InvocationHandler.
201: * </p>
202: *
203: * <p>
204: * Each Component returned by this Provider is wrapped inside a
205: * Proxy class which implements all of the Component's interfaces
206: * including javax.xml.rpc.server.ServiceLifecycle.
207: * </p>
208: *
209: * <p>
210: * When Axis is finished with the object returned by this provider,
211: * it invokes ServiceLifecycle.destroy(). This is intercepted by the
212: * InvocationHandler and the Component is returned at this time back
213: * to the ComponentManager it was retrieved from.
214: * </p>
215: *
216: * <p>
217: * <b>Note</b>, when Axis invokes ServiceLifecycle.destroy() is dependant
218: * on the scope of the service (ie. Request, Session & Application).
219: * </p>
220: */
221: final static class Handler implements InvocationHandler {
222:
223: // Constants describing the ServiceLifecycle.destroy method
224: private final static String SL_DESTROY = "destroy";
225: private final Class SL_CLASS = ServiceLifecycle.class;
226:
227: // Component & ComponentManager references
228: private final Component m_object;
229: private final ComponentManager m_manager;
230:
231: /**
232: * Simple constructor, sets all internal references
233: *
234: * @param object a <code>Component</code> instance
235: * @param manager a <code>ComponentManager</code> instance
236: */
237: public Handler(final Component object,
238: final ComponentManager manager) {
239: m_object = object;
240: m_manager = manager;
241: }
242:
243: /**
244: * <code>invoke</code> method, handles all method invocations for this
245: * particular proxy.
246: *
247: * <p>
248: * Usually the invocation is passed through to the
249: * actual component the proxy wraps, unless the method belongs to
250: * the <code>ServiceLifecycle</code> interface where it is handled
251: * locally.
252: * </p>
253: *
254: * @param proxy the <code>Proxy</code> instance the method was invoked on
255: * @param method the invoked method <code>Method</code> object
256: * @param args an <code>Object[]</code> array of arguments
257: * @return an <code>Object</code> value or null if none
258: * @exception Throwable if an error occurs
259: */
260: public Object invoke(Object proxy, Method method, Object[] args)
261: throws Throwable {
262: // if ServiceLifecycle.destroy() called, return to CM
263: if (method.getDeclaringClass().equals(SL_CLASS)) {
264: if (method.getName().equals(SL_DESTROY)) {
265: m_manager.release(m_object);
266: }
267:
268: return null;
269: }
270: // otherwise pass call to the real object
271: return method.invoke(m_object, args);
272: }
273: }
274: }
|