Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » net » axis » server » [javadoc | source]
    1   /*
    2    * JBoss, the OpenSource J2EE webOS
    3    *
    4    * Distributable under LGPL license.
    5    * See terms of license at gnu.org.
    6    */
    7   
    8   // $Id: EJBProvider.java,v 1.3.4.4 2003/03/28 12:50:46 cgjung Exp $
    9   
   10   package org.jboss.net.axis.server;
   11   
   12   import java.lang.reflect.Method;
   13   import java.util.ArrayList;
   14   import java.util.Iterator;
   15   
   16   import javax.naming.NamingException;
   17   import javax.servlet.http.HttpSessionBindingEvent;
   18   import javax.servlet.http.HttpSessionBindingListener;
   19   import javax.xml.namespace.QName;
   20   import javax.xml.rpc.server.ServiceLifecycle;
   21   
   22   import org.apache.axis.AxisFault;
   23   import org.apache.axis.EngineConfiguration;
   24   import org.apache.axis.MessageContext;
   25   import org.apache.axis.description.OperationDesc;
   26   import org.apache.axis.description.ServiceDesc;
   27   import org.apache.axis.handlers.soap.SOAPService;
   28   import org.apache.axis.message.SOAPEnvelope;
   29   
   30   import org.jboss.net.axis.XMLResourceProvider;
   31   
   32   /**
   33    * <p>
   34    * A JBoss-compatible EJB Provider that exposes the methods of
   35    * any session bean as a web service endpoint. 
   36    * </p>
   37    * <p>
   38    * Basically it is a slimmed downed derivative of 
   39    * the Axis-EJBProvider without the usual, corba-related configuration mumbo-jumbo
   40    * that is operating under the presumption that the right classloader has already been set
   41    * by the request flow chain (@see org.jboss.net.axis.SetClassLoaderHandler).
   42    * </p>
   43    * <p>
   44    * Since Version 1.5 and thanks to Kevin Conner, we now also support 
   45    * stateful beans that are tied to the service scope (you should reasonably 
   46    * choose scope="session" in the <service/> tag of the corresponding web-service.xml) 
   47    * Note that by using a jaxp lifecycle handler we synchronize with 
   48    * web service scopes that can be shorter-lived than the surrounding http-session. 
   49    * However, as I understood Kevin and from my observations, it seems that Axis 
   50    * and hence the jaxp lifecycle does not get notified upon http-session expiration. 
   51    * Hence our lifecycle listener currently implements the http session 
   52    * lifecycle, too (which is not very transport-agnostic, but who cares 
   53    * at the moment).
   54    * </p>  
   55    * <p>
   56    * EJBProvider is able to recognize an {@link WsdlAwareHttpActionHandler} in its
   57    * transport chain such that it will set the soap-action headers in the wsdl.
   58    * </p>
   59    * @author <a href="mailto:Christoph.Jung@infor.de">Christoph G. Jung</a>
   60    * @since 5. Oktober 2001, 13:02
   61    * @version $Revision: 1.3.4.4 $
   62    */
   63   
   64   public class EJBProvider extends org.apache.axis.providers.java.EJBProvider {
   65   
   66      //
   67      // Attributes
   68      //
   69   
   70      /** the real remote class we are shielding */
   71      protected Class remoteClass;
   72   
   73      /** we are caching the home for perfomance purposes */
   74      protected Object ejbHome;
   75   
   76      /** we are caching the create method for perfomance purposes */
   77      protected Method ejbCreateMethod;
   78   
   79      //
   80      // Constructors
   81      //
   82   
   83      /** Creates new EJBProvider */
   84      public EJBProvider() {
   85      }
   86   
   87      //
   88      // Helper Methods
   89      //
   90   
   91      /**
   92       * access home factory via jndi if 
   93       * reference is not yet cached
   94       */
   95   
   96      protected synchronized Object getEJBHome(String jndiName)
   97         throws NamingException {
   98         if (ejbHome == null) {
   99            // Get the EJB Home object from JNDI
  100            ejbHome = this.getCachedContext().lookup(jndiName);
  101         }
  102         return ejbHome;
  103      }
  104   
  105      /**
  106       * access home factory via jndi if 
  107       * reference is not yet cached
  108       */
  109   
  110      protected synchronized Method getEJBCreateMethod(String jndiName)
  111         throws NamingException, NoSuchMethodException {
  112         if (ejbCreateMethod == null) {
  113            Object ejbHome = getEJBHome(jndiName);
  114            ejbCreateMethod =
  115               ejbHome.getClass().getMethod("create", empty_class_array);
  116         }
  117   
  118         return ejbCreateMethod;
  119      }
  120   
  121      /**
  122       * Return the object which implements the service lifecycle. Makes the usual
  123       * lookup->create call w/o the PortableRemoteDaDaDa for the sake of Corba.
  124       * @param msgContext the message context
  125       * @param clsName The JNDI name of the EJB home class
  126       * @return an object that implements the service
  127       */
  128      protected Object makeNewServiceObject(
  129         MessageContext msgContext,
  130         String clsName)
  131         throws Exception {
  132   
  133         // abuse the clsName as jndi lookup name
  134         Object ejbHome = getEJBHome(clsName);
  135         // Invoke the create method of the ejbHome class without actually
  136         // touching any EJB classes (i.e. no cast to EJBHome)
  137         Method createMethod = getEJBCreateMethod(clsName);
  138         // shield behind the lifecycle service
  139         return new EJBServiceLifeCycle(
  140            createMethod.invoke(ejbHome, empty_object_array));
  141      }
  142   
  143      /**
  144       * Return the class name of the service, note that this could be called
  145       * outside the correct chain, e.g., by the url mapper. Hence we need
  146       * to find the right classloader.
  147       */
  148   
  149      protected synchronized Class getServiceClass(
  150         String beanJndiName,
  151         SOAPService service,
  152         MessageContext msgContext)
  153         throws AxisFault {
  154         if (remoteClass == null) {
  155   
  156            // we need to find the right service classloader first
  157            // through the engine
  158            EngineConfiguration engineConfig =
  159               msgContext != null ? msgContext.getAxisEngine().getConfig() : null;
  160   
  161            ClassLoader currentLoader =
  162               Thread.currentThread().getContextClassLoader();
  163   
  164            if (engineConfig != null
  165               && engineConfig instanceof XMLResourceProvider) {
  166               XMLResourceProvider config = (XMLResourceProvider) engineConfig;
  167   
  168               ClassLoader newLoader =
  169                  config.getMyDeployment().getClassLoader(
  170                     new QName(null, service.getName()));
  171   
  172               // enter the classloader
  173               Thread.currentThread().setContextClassLoader(newLoader);
  174            }
  175   
  176            try {
  177               remoteClass = getEJBCreateMethod(beanJndiName).getReturnType();
  178            } catch (NamingException e) {
  179               throw new AxisFault("Could not find home in JNDI", e);
  180            } catch (NoSuchMethodException e) {
  181               throw new AxisFault("Could not find create method at home ;-)", e);
  182            } finally {
  183               Thread.currentThread().setContextClassLoader(currentLoader);
  184            }
  185   
  186         }
  187   
  188         return remoteClass;
  189      }
  190   
  191      //
  192      // Public API
  193      //
  194   
  195      /**
  196       * Generate the WSDL for this service.
  197       * We need to rearrange the classloader stuff for that purpose similar
  198       * as we did with the service class interface
  199       */
  200   
  201      public void generateWSDL(MessageContext msgContext) throws AxisFault {
  202         EngineConfiguration engineConfig =
  203            msgContext != null ? msgContext.getAxisEngine().getConfig() : null;
  204   
  205         ClassLoader currentLoader =
  206            Thread.currentThread().getContextClassLoader();
  207   
  208         if (engineConfig != null
  209            && engineConfig instanceof XMLResourceProvider) {
  210            XMLResourceProvider config = (XMLResourceProvider) engineConfig;
  211            ClassLoader newLoader =
  212               config.getMyDeployment().getClassLoader(
  213                  new QName(null, msgContext.getTargetService()));
  214            Thread.currentThread().setContextClassLoader(newLoader);
  215         }
  216   
  217         try {
  218   
  219            // check whether there is an http action header present
  220            if (msgContext != null) {
  221   
  222               boolean isSoapAction =
  223                  msgContext.getProperty(
  224                     Constants.ACTION_HANDLER_PRESENT_PROPERTY)
  225                     == Boolean.TRUE;
  226   
  227               // yes, then loop through the operation descriptions
  228               for (Iterator alloperations =
  229                  msgContext
  230                     .getService()
  231                     .getServiceDescription()
  232                     .getOperations()
  233                     .iterator();
  234                  alloperations.hasNext();
  235                  ) {
  236                  OperationDesc opDesc = (OperationDesc) alloperations.next();
  237                  // and add a soap action tag with the name of the service
  238                  opDesc.setSoapAction(
  239                     isSoapAction ? msgContext.getService().getName() : null);
  240               }
  241            }
  242   
  243            super.generateWSDL(msgContext);
  244   
  245         } finally {
  246            Thread.currentThread().setContextClassLoader(currentLoader);
  247         }
  248      }
  249   
  250      /**
  251       * Override processMessage of super class in order
  252       * to unpack the service object from the lifecycle
  253       */
  254      public void processMessage(
  255         MessageContext msgContext,
  256         SOAPEnvelope reqEnv,
  257         SOAPEnvelope resEnv,
  258         Object obj)
  259         throws Exception {
  260         super.processMessage(
  261            msgContext,
  262            reqEnv,
  263            resEnv,
  264            ((EJBServiceLifeCycle) obj).serviceObject);
  265      }
  266   
  267      /*
  268       * Adds the correct stop classes and soap-action annotations to wsdl generation 
  269       * @see org.apache.axis.providers.java.EJBProvider#initServiceDesc(org.apache.axis.handlers.soap.SOAPService,org.apache.axis.MessageContext)
  270       */
  271      public void initServiceDesc(SOAPService service, MessageContext msgContext)
  272         throws AxisFault {
  273         // the service class used to fill service description is the EJB Remote/Local Interface
  274         // we add EJBObject and EJBLocalObject as stop classes because we		 
  275         // don't want any of their methods in the wsdl ...
  276         ServiceDesc serviceDescription = service.getServiceDescription();
  277         ArrayList stopClasses = serviceDescription.getStopClasses();
  278         if (stopClasses == null)
  279            stopClasses = new ArrayList();
  280         stopClasses.add("javax.ejb.EJBObject");
  281         stopClasses.add("javax.ejb.EJBLocalObject");
  282         serviceDescription.setStopClasses(stopClasses);
  283         // once the stop classes are right, we can generate meta-data for only
  284         // the wanted methods
  285         super.initServiceDesc(service, msgContext);
  286      }
  287   
  288      //
  289      // Inner Classes
  290      //
  291   
  292      /**
  293       * This is the lifecycle object that is registered in the 
  294       * message scope and that shields the proper bean reference
  295       */
  296   
  297      protected static class EJBServiceLifeCycle
  298         implements ServiceLifecycle, HttpSessionBindingListener {
  299   
  300         //
  301         // Attributes
  302         //
  303   
  304         /** may be local or remote object */
  305         protected Object serviceObject;
  306   
  307         //
  308         // Constructors
  309         //
  310   
  311         /** constructs a new lifecycle */
  312         protected EJBServiceLifeCycle(Object serviceObject) {
  313            this.serviceObject = serviceObject;
  314         }
  315   
  316         //
  317         // Public API
  318         //
  319   
  320         /**
  321          * call remove 
  322          * @see javax.xml.rpc.server.ServiceLifecycle#destroy()
  323          */
  324         public void destroy() {
  325            try {
  326               if (serviceObject instanceof javax.ejb.EJBObject) {
  327                  try {
  328                     ((javax.ejb.EJBObject) serviceObject).remove();
  329                  } catch (java.rmi.RemoteException e) {
  330                     // have yet to decide what to do
  331                  }
  332               } else {
  333                  ((javax.ejb.EJBLocalObject) serviceObject).remove();
  334               }
  335            } catch (javax.ejb.RemoveException e) {
  336               // have yet to decide what to do
  337            }
  338         }
  339   
  340         /**
  341          * Nothing to be done
  342          * @see javax.xml.rpc.server.ServiceLifecycle#init(Object)
  343          */
  344         public void init(Object arg0) {
  345         }
  346   
  347         /**
  348          * @see javax.servlet.http.HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)
  349          */
  350         public void valueBound(HttpSessionBindingEvent arg0) {
  351            init(arg0);
  352         }
  353   
  354         /**
  355          * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)
  356          */
  357         public void valueUnbound(HttpSessionBindingEvent arg0) {
  358            destroy();
  359         }
  360   
  361      } // EJBServiceLifeCycle
  362   
  363   } // EJBProvider

Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » net » axis » server » [javadoc | source]