EJBProvider.java |
/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ // $Id: EJBProvider.java,v 1.20.4.2 2005/03/02 14:19:51 tdiesler Exp $ package org.jboss.net.axis.server; import org.jboss.axis.AxisFault; import org.jboss.axis.MessageContext; import org.jboss.axis.description.OperationDesc; import org.jboss.axis.description.ServiceDesc; import org.jboss.axis.handlers.soap.SOAPService; import org.jboss.axis.message.SOAPEnvelopeAxisImpl; import javax.naming.NamingException; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import javax.xml.rpc.server.ServiceLifecycle; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Iterator; /** * <p> * A JBoss-compatible EJB Provider that exposes the methods of * any session bean as a web service endpoint. * </p> * <p> * Basically it is a slimmed downed derivative of * the Axis-EJBProvider without the usual, corba-related configuration mumbo-jumbo * that is operating under the presumption that the right classloader has already been set * by the request flow chain (@see org.jboss.net.axis.SetClassLoaderHandler). * </p> * <p> * Since Version 1.5 and thanks to Kevin Conner, we now also support * stateful beans that are tied to the service scope (you should reasonably * choose scope="session" in the <service/> tag of the corresponding web-service.xml) * Note that by using a jaxp lifecycle handler we synchronize with * web service scopes that can be shorter-lived than the surrounding http-session. * However, as I understood Kevin and from my observations, it seems that Axis * and hence the jaxp lifecycle does not get notified upon http-session expiration. * Hence our lifecycle listener currently implements the http session * lifecycle, too (which is not very transport-agnostic, but who cares * at the moment). * </p> * <p> * EJBProvider is able to recognize an {@link WsdlAwareHttpActionHandler} in its * transport chain such that it will set the soap-action headers in the wsdl. * </p> * @author <a href="mailto:Christoph.Jung@infor.de">Christoph G. Jung</a> * @since 5. Oktober 2001, 13:02 * @version $Revision: 1.20.4.2 $ */ public class EJBProvider extends org.jboss.axis.providers.java.EJBProvider { // // Attributes // /** the real remote class we are shielding */ protected Class remoteClass; /** we are caching the home for perfomance purposes */ protected Object ejbHome; /** we are caching the create method for perfomance purposes */ protected Method ejbCreateMethod; // // Constructors // /** Creates new EJBProvider */ public EJBProvider() { } // // Helper Methods // /** * access home factory via jndi if * reference is not yet cached */ protected synchronized Object getEJBHome(String jndiName) throws NamingException { if (ejbHome == null) { // Get the EJB Home object from JNDI ejbHome = this.getCachedContext().lookup(jndiName); } return ejbHome; } /** * access home factory via jndi if * reference is not yet cached */ protected synchronized Method getEJBCreateMethod(String jndiName) throws NamingException, NoSuchMethodException { if (ejbCreateMethod == null) { Object ejbHome = getEJBHome(jndiName); ejbCreateMethod = ejbHome.getClass().getMethod("create", empty_class_array); } return ejbCreateMethod; } /** * Return the object which implements the service lifecycle. Makes the usual * lookup->create call w/o the PortableRemoteDaDaDa for the sake of Corba. * @param msgContext the message context * @param clsName The JNDI name of the EJB home class * @return an object that implements the service */ protected Object makeNewServiceObject(MessageContext msgContext, String clsName) throws Exception { // abuse the clsName as jndi lookup name Object ejbHome = getEJBHome(clsName); // Invoke the create method of the ejbHome class without actually // touching any EJB classes (i.e. no cast to EJBHome) Method createMethod = getEJBCreateMethod(clsName); // shield behind the lifecycle service return new EJBServiceLifeCycle(createMethod.invoke(ejbHome, empty_object_array)); } /** * Return the class name of the service, note that this could be called * outside the correct chain, e.g., by the url mapper. Hence we need * to find the right classloader. */ protected synchronized Class getServiceClass(String beanJndiName, SOAPService service, MessageContext msgContext) throws AxisFault { if (remoteClass == null) { try { remoteClass = getEJBCreateMethod(beanJndiName).getReturnType(); } catch (NamingException e) { throw new AxisFault("Could not find home in JNDI", e); } catch (NoSuchMethodException e) { throw new AxisFault("Could not find create method at home ;-)", e); } } return remoteClass; } // // Public API // /** * Generate the WSDL for this service. * We need to rearrange the classloader stuff for that purpose similar * as we did with the service class interface */ public void generateWSDL(MessageContext msgContext) throws AxisFault { // check whether there is an http action header present if (msgContext != null) { boolean isSoapAction = msgContext.getProperty(Constants.ACTION_HANDLER_PRESENT_PROPERTY) == Boolean.TRUE; // yes, then loop through the operation descriptions for (Iterator alloperations = msgContext .getService() .getServiceDescription() .getOperations() .iterator(); alloperations.hasNext(); ) { OperationDesc opDesc = (OperationDesc)alloperations.next(); // and add a soap action tag with the name of the service opDesc.setSoapAction(isSoapAction ? msgContext.getService().getName() : null); } } super.generateWSDL(msgContext); } /** * Override processMessage of super class in order * to unpack the service object from the lifecycle */ public void processMessage(MessageContext msgContext, SOAPEnvelopeAxisImpl reqEnv, SOAPEnvelopeAxisImpl resEnv, Object obj) throws Exception { super.processMessage(msgContext, reqEnv, resEnv, ((EJBServiceLifeCycle)obj).serviceObject); } /* * Adds the correct stop classes and soap-action annotations to wsdl generation * @see org.jboss.axis.providers.java.EJBProvider#initServiceDesc(org.jboss.axis.handlers.soap.SOAPService,org.jboss.axis.MessageContext) */ public void initServiceDesc(SOAPService service, MessageContext msgContext) throws AxisFault { // The service class used to fill service description is the EJB Remote/Local Interface // we add EJBObject and EJBLocalObject as stop classes because we don't want any of their methods in the wsdl ... // once the stop classes are right, we can generate meta-data for only the wanted methods ServiceDesc serviceDescription = service.getServiceDescription(); ArrayList stopClasses = serviceDescription.getStopClasses(); if (stopClasses == null) stopClasses = new ArrayList(); stopClasses.add("javax.ejb.EJBObject"); stopClasses.add("javax.ejb.EJBLocalObject"); serviceDescription.setStopClasses(stopClasses); super.initServiceDesc(service, msgContext); } // // Inner Classes // /** * This is the lifecycle object that is registered in the * message scope and that shields the proper bean reference */ protected static class EJBServiceLifeCycle implements ServiceLifecycle, HttpSessionBindingListener { // // Attributes // /** may be local or remote object */ protected Object serviceObject; // // Constructors // /** constructs a new lifecycle */ protected EJBServiceLifeCycle(Object serviceObject) { this.serviceObject = serviceObject; } // // Public API // /** * call remove * @see javax.xml.rpc.server.ServiceLifecycle#destroy() */ public void destroy() { try { if (serviceObject instanceof javax.ejb.EJBObject) { try { ((javax.ejb.EJBObject)serviceObject).remove(); } catch (java.rmi.RemoteException e) { } } else { ((javax.ejb.EJBLocalObject)serviceObject).remove(); } } catch (javax.ejb.RemoveException e) { } catch (Exception e) { // if we keep the instance too long, removal might happen after // undeployment of the bean. Also the cached bean reference might be // invalid after redeployment of the referenced bean. // In such a case method invocation can cause different runtime // exceptions in jboss (e.g. NullPointerException, // UndeclaredThrowableException, ..). Anyway as we are no longer using // the bean reference these exceptions can be ignored. } } /** * Nothing to be done * @see javax.xml.rpc.server.ServiceLifecycle#init(Object) */ public void init(Object arg0) { } /** * @see javax.servlet.http.HttpSessionBindingListener#valueBound(HttpSessionBindingEvent) */ public void valueBound(HttpSessionBindingEvent arg0) { init(arg0); } /** * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent) */ public void valueUnbound(HttpSessionBindingEvent arg0) { destroy(); } } // EJBServiceLifeCycle } // EJBProvider
EJBProvider.java |