| 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 |