Package org.jboss.ejb3.session

Source Code of org.jboss.ejb3.session.SessionSpecContainer$AsyncInvocationTask

/*
* JBoss, Home of Professional Open Source.
* Copyright 2010, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ejb3.session;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import javax.ejb.EJBLocalObject;
import javax.ejb.EJBObject;
import javax.ejb.Handle;
import javax.ejb.RemoveException;

import org.jboss.aop.Domain;
import org.jboss.aop.MethodInfo;
import org.jboss.aop.util.MethodHashing;
import org.jboss.beans.metadata.api.annotations.Inject;
import org.jboss.ejb3.Ejb3Deployment;
import org.jboss.ejb3.async.impl.AsyncInvocationIdUUIDImpl;
import org.jboss.ejb3.async.impl.util.concurrent.ResultUnwrappingExecutorService;
import org.jboss.ejb3.async.spi.AsyncEndpoint;
import org.jboss.ejb3.async.spi.AsyncInvocationId;
import org.jboss.ejb3.async.spi.AsyncInvocationMap;
import org.jboss.ejb3.async.spi.AsyncInvocationTaskBase;
import org.jboss.ejb3.async.spi.CurrentAsyncInvocation;
import org.jboss.ejb3.common.lang.SerializableMethod;
import org.jboss.ejb3.common.registrar.spi.Ejb3RegistrarLocator;
import org.jboss.ejb3.core.businessobject.BusinessObjectFactory;
import org.jboss.ejb3.core.proxy.spi.CurrentRemoteProxyFactory;
import org.jboss.ejb3.core.proxy.spi.RemoteProxyFactory;
import org.jboss.ejb3.endpoint.Endpoint;
import org.jboss.ejb3.proxy.impl.factory.session.SessionProxyFactory;
import org.jboss.ejb3.proxy.impl.factory.session.SessionSpecProxyFactory;
import org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandler;
import org.jboss.ejb3.proxy.impl.remoting.SessionSpecRemotingMetadata;
import org.jboss.ejb3.proxy.spi.container.InvokableContext;
import org.jboss.ejb3.stateful.StatefulContainer;
import org.jboss.ejb3.stateful.StatefulContainerInvocation;
import org.jboss.logging.Logger;
import org.jboss.metadata.ejb.jboss.JBossSessionBeanMetaData;
import org.jboss.security.SecurityContext;

/**
* SessionSpecContainer
*
* A SessionContainer with support for Session Beans defined
* specifically by the EJB3 Specification
*
* @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
* @version $Revision: $
*/
public abstract class SessionSpecContainer extends SessionContainer implements InvokableContext, AsyncEndpoint
{

   // ------------------------------------------------------------------------------||
   // Class Members ----------------------------------------------------------------||
   // ------------------------------------------------------------------------------||

   private static final Logger log = Logger.getLogger(SessionSpecContainer.class);

   /**
    * Business interfaces for this EJB
    */
   private Set<Class<?>> businessInterfaces;

   /**
    * {@link ExecutorService} used for EJB 3.1 Async invocations
    */
   private final ExecutorService asynchronousExecutor;

   private BusinessObjectFactory businessObjectFactory;

   private AsyncInvocationMap currentAsyncInvocations;

   // ------------------------------------------------------------------------------||
   // Constructor ------------------------------------------------------------------||
   // ------------------------------------------------------------------------------||

   public SessionSpecContainer(ClassLoader cl, String beanClassName, String ejbName, Domain domain,
         Hashtable ctxProperties, Ejb3Deployment deployment, JBossSessionBeanMetaData beanMetaData,
         final ExecutorService asynchronousExecutor) throws ClassNotFoundException
   {
      super(cl, beanClassName, ejbName, domain, ctxProperties, deployment, beanMetaData);
      if (asynchronousExecutor == null)
      {
         throw new IllegalArgumentException("Asynchronous Executor must be specified");
      }
      this.asynchronousExecutor = new ResultUnwrappingExecutorService(asynchronousExecutor, this);
   }

   public SessionSpecContainer(ClassLoader cl, String beanClassName, String ejbName, Domain domain,
         Hashtable ctxProperties, JBossSessionBeanMetaData beanMetaData, final ExecutorService asynchronousExecutor)
         throws ClassNotFoundException
   {
      super(cl, beanClassName, ejbName, domain, ctxProperties, beanMetaData);
      if (asynchronousExecutor == null)
      {
         throw new IllegalArgumentException("Asynchronous Executor must be specified");
      }
      this.asynchronousExecutor = new ResultUnwrappingExecutorService(asynchronousExecutor, this);
   }

   /**
    * Invokes the specified method upon the specified session, passing the specified
    * arguments.
    *
    * This is required by the {@link Endpoint} interface and is the correct implementation
    * of the ejb3-core containers looking forward.
    *
    * @param session
    * @param invokedBusinessInterface
    * @param method
    * @param args
    * @see org.jboss.ejb3.endpoint.Endpoint#invoke(java.io.Serializable, java.lang.Class, java.lang.reflect.Method, java.lang.Object[])
    */
   public Object invoke(final Serializable session, final Class<?> invokedBusinessInterface, final Method method,
         final Object[] args) throws Throwable
   {
      /*
       * For now we'll just delegate to the legacy implementation
       * defined by InvokableContext.invoke; in the future this method
       * will be the real handler
       */
      //TODO Move away from InvokableContext contract EJBTHREE-1782

      // Create a SerializableMethod view
      SerializableMethod sMethod = new SerializableMethod(method, invokedBusinessInterface);

      // Handle in the transition method
      return this.invoke(session, sMethod, args);
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.ejb3.async.spi.AsyncEndpoint#invokeAsync(java.io.Serializable, java.lang.Class, java.lang.reflect.Method, java.lang.Object[])
    */
   @Override
   public Object invokeAsync(final Serializable session, final Class<?> invokedBusinessInterface, final Method method,
         final Object[] args) throws Throwable
   {
      // Get the ES
      final ExecutorService es = this.getAsynchronousExecutor();
     
      // Get the Security Context
      final SecurityContext sc = SecurityActions.getSecurityContext();

      // Make a new ID for the invocation
      final AsyncInvocationId id = new AsyncInvocationIdUUIDImpl();

      // Submit as async
      final Future<?> returnValue = es.submit(new AsyncInvocationTask<Object>(session,method,args, sc, id));

      // Return
      return returnValue;
   }

   /**
    * Task used to dispatch a local invocation upon the server
    * asynchronously
    * @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
    */
   private class AsyncInvocationTask<V> extends AsyncInvocationTaskBase<V>
   {

      private final Serializable session;

      private final Method method;

      private final Object[] args;

      AsyncInvocationTask(final Serializable session, final Method method, final Object[] args,
            final SecurityContext sc, final AsyncInvocationId id)
      {
         super(sc, id, AccessController.doPrivileged(new PrivilegedAction<ClassLoader>()
         {

            @Override
            public ClassLoader run()
            {
               return Thread.currentThread().getContextClassLoader();
            }
         }));
         this.session = session;
         this.method = method;
         this.args = args;
      }

      /**
       * {@inheritDoc}
       * @see org.jboss.ejb3.async.spi.AsyncInvocationTaskBase#before()
       */
      @Override
      protected void before() throws Exception
      {
         // Mark the current invocation on the executing Thread
         CurrentAsyncInvocation.markCurrentInvocationOnThread(this.id);
      }

      /**
       * {@inheritDoc}
       * @see org.jboss.ejb3.async.spi.AsyncInvocationTaskBase#proceed()
       */
      @Override
      @SuppressWarnings("unchecked")
      protected V proceed() throws Throwable
      {
         // Invoke on the container
         return (V) SessionSpecContainer.this.invoke(session, null, method, args);
      }

      /**
       * {@inheritDoc}
       * @see org.jboss.ejb3.async.spi.AsyncInvocationTaskBase#after()
       */
      @Override
      protected void after() throws Exception
      {
         // Unmark the current invocation from the executing Thread
         CurrentAsyncInvocation.unmarkCurrentInvocationFromThread();
      }

   }

   /**
    * A transition method in moving from InvokableContext.invoke to Endpoint.invoke.
    *
    * Invokes the specified method upon the specified session, passing the specified
    * arguments
    *
    * @param session
    * @param method
    * @param args
    * @return
    * @throws Throwable
    */
   @Deprecated
   public Object invoke(final Serializable session, final SerializableMethod method, final Object[] args)
         throws Throwable
   {
      /*
       * Replace the TCL with the CL for this Container
       */
      ClassLoader oldLoader = SecurityActions.getContextClassLoader();

      SecurityActions.setContextClassLoader(this.getClassloader());

      /*
       * Obtain the target method (advised)
       */
      Method actualMethod = method.toMethod(this.getClassloader());
      long hash = MethodHashing.calculateHash(actualMethod);
      MethodInfo info = getAdvisor().getMethodInfo(hash);
      if (info == null)
      {
         throw new RuntimeException("Method invocation via Proxy could not be found handled for EJB "
               + this.getEjbName() + " : " + method.toString()
               + ", probable error in virtual method registration w/ Advisor for the Container");
      }
      Method unadvisedMethod = info.getUnadvisedMethod();
      SerializableMethod unadvisedSerializableMethod = new SerializableMethod(unadvisedMethod);

      // Mark the start time
      long start = System.currentTimeMillis();

      try
      {
         Class<?> invokedBusinessInterface = Class.forName(method.getActualClassName(), false, getClassloader());
         // EJBTHREE-2076 if it's a EJB2.x view invocation, then invoked business interface should be null.
         if (this.isEjb2xView(invokedBusinessInterface.getName()))
         {
            invokedBusinessInterface = null;
         }

         // Increment invocation statistics
         invokeStats.callIn();

         /*
          * Invoke directly if this is an EJB2.x Method
          */

         if (unadvisedMethod != null && isHomeMethod(unadvisedSerializableMethod))
         {
            return invokeHomeMethod(actualMethod, args);
         }
         else if (unadvisedMethod != null && this.isEjbObjectMethod(unadvisedSerializableMethod))
         {
            return invokeEJBObjectMethod(session, info, args);
         }

         // FIXME: Ahem, stateful container invocation works on all.... (violating contract though)        
         /*
          * Build an invocation
          */

         StatefulContainerInvocation nextInvocation = new StatefulContainerInvocation(info, session,
               invokedBusinessInterface, this.getAsynchronousExecutor(), this);
         nextInvocation.getMetaData().addMetaData(SessionSpecRemotingMetadata.TAG_SESSION_INVOCATION,
               SessionSpecRemotingMetadata.KEY_INVOKED_METHOD, method);
         nextInvocation.setArguments(args);

         /*
          * Invoke
          */

         return nextInvocation.invokeNext();

      }
      finally
      {
         /*
          * Update Invocation Statistics
          */
         if (unadvisedMethod != null)
         {
            // Mark end time
            long end = System.currentTimeMillis();

            // Calculate elapsed time
            long elapsed = end - start;

            // Update statistics with elapsed time
            invokeStats.updateStats(unadvisedMethod, elapsed);
         }

         // Complete call to increment statistics
         invokeStats.callOut();

         SecurityActions.setContextClassLoader(oldLoader);
      }
   }

   /**
    * Invokes the method described by the specified serializable method
    * as called from the specified proxy, using the specified arguments
    *
    * @param proxy The proxy making the invocation
    * @param method The method to be invoked
    * @param args The arguments to the invocation
    * @throws Throwable A possible exception thrown by the invocation
    * @return
    */
   public Object invoke(Object proxy, SerializableMethod method, Object[] args) throws Throwable
   {
      /*
       *  Obtain Session ID
       */
      Serializable sessionId = null;

      // If coming from ejb3-proxy-impl
      if (Proxy.isProxyClass(proxy.getClass()))
      {
         InvocationHandler handler = Proxy.getInvocationHandler(proxy);
         assert handler instanceof SessionProxyInvocationHandler : "Requires "
               + SessionProxyInvocationHandler.class.getName();
         SessionProxyInvocationHandler sHandler = (SessionProxyInvocationHandler) handler;
         sessionId = (Serializable) sHandler.getTarget();
      }

      //TODO Session ID if nointerface

      // Send along to the transition method
      return this.invoke(sessionId, method, args);
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.ejb3.async.spi.AsyncCancellableContext#cancel(org.jboss.ejb3.async.spi.AsyncInvocationId)
    */
   @Override
   public boolean cancel(final AsyncInvocationId id) throws IllegalArgumentException
   {
      // Precondition checks
      if (id == null)
      {
         throw new IllegalArgumentException("ID must be specified");
      }

      // Put a flag in the cancel map, will be cleared when the invocation passes through
      final AsyncInvocationMap map = this.getCurrentAsyncInvocations();
      return map.put(id, true) != null;
   }

   /**
    * Provides implementation for this bean's EJB 2.1 Home.create() method
    *
    * @param factory
    * @param unadvisedMethod
    * @param args
    * @return
    * @throws Exception
    */
   protected Object invokeHomeCreate(Method method, Object args[]) throws Exception
   {

      /*
       * Initialize
       */

      // Hold the JNDI Name
      String jndiName = null;

      // Flag for if we've found the interface
      boolean foundInterface = false;

      // Name of the EJB2.x Interface Class expected
      String ejb2xInterface = method.getReturnType().getName();

      // Get Metadata
      JBossSessionBeanMetaData smd = this.getMetaData();

      /*
       * Determine if the expected type is found in metadata as a EJB2.x Interface
       */

      // Is this a Remote Interface ?
      boolean isLocal = false;
      String ejb2xRemoteInterface = smd.getRemote();
      if (ejb2xInterface.equals(ejb2xRemoteInterface))
      {
         // We've found it, it's false
         foundInterface = true;
         jndiName = smd.getJndiName();
      }

      // Is this a local interface?
      if (!foundInterface)
      {
         String ejb2xLocalInterface = smd.getLocal();
         if (ejb2xInterface.equals(ejb2xLocalInterface))
         {
            // Mark as found
            foundInterface = true;
            isLocal = true;
            jndiName = smd.getLocalJndiName();
         }
      }

      // If we haven't yet found the interface
      if (!foundInterface)
      {
         throw new RuntimeException("Specified return value for " + method + " notes an EJB 2.x interface: "
               + ejb2xInterface + "; this could not be found as either a valid remote or local interface for EJB "
               + this.getEjbName());
      }

      // Allow override of the remote proxy
      if (!isLocal)
      {
         RemoteProxyFactory remoteProxyFactory = CurrentRemoteProxyFactory.get();
         if (remoteProxyFactory != null)
            return remoteProxyFactory.create(null);
      }

      // Lookup
      String proxyFactoryKey = this.getJndiRegistrar().getProxyFactoryRegistryKey(jndiName, smd, isLocal);
      Object factory = Ejb3RegistrarLocator.locateRegistrar().lookup(proxyFactoryKey);

      // Cast
      assert factory instanceof SessionProxyFactory : "Specified factory " + factory.getClass().getName()
            + " is not of type " + SessionProxyFactory.class.getName() + " as required by "
            + StatefulContainer.class.getName() + ", but was instead " + factory;
      SessionSpecProxyFactory sessionFactory = null;
      sessionFactory = SessionSpecProxyFactory.class.cast(factory);

      // Create Proxy
      Object proxy = sessionFactory.createProxyEjb2x();

      // Return
      return proxy;
   }

   /**
    * TODO: work in progress (refactor both invokeHomeMethod's, localHomeInvoke)
    */
   //TODO
   private Object invokeHomeMethod(Method method, Object args[]) throws Exception
   {
      if (method.getName().equals(Ejb2xMethodNames.METHOD_NAME_HOME_CREATE))
      {
         return this.invokeHomeCreate(method, args);
      }
      else if (method.getName().equals(Ejb2xMethodNames.METHOD_NAME_HOME_REMOVE))
      {
         if (args[0] instanceof Handle)
            removeHandle((Handle) args[0]);
         else
         {
            throw new RemoveException(
                  "EJB 3.0 Specification Violation 3.6.2.2: Session beans do not have a primary key");
         }

         return null;
      }
      else
      {
         throw new IllegalArgumentException("illegal home method " + method);
      }
   }

   /**
    * @deprecated Use isHomeMethod(SerializableMethod method) in SessionSpecContainer
    */
   @Deprecated
   protected boolean isHomeMethod(Method method)
   {
      if (javax.ejb.EJBHome.class.isAssignableFrom(method.getDeclaringClass()))
         return true;
      if (javax.ejb.EJBLocalHome.class.isAssignableFrom(method.getDeclaringClass()))
         return true;
      return false;
   }

   /**
    * Determines whether the specified method is an EJB2.x Home Method
    *
    * @param method
    * @return
    */
   protected boolean isHomeMethod(SerializableMethod method)
   {
      // Get the Method
      Method invokingMethod = method.toMethod(this.getClassloader());

      // Use legacy
      return this.isHomeMethod(invokingMethod);
   }

   /**
    * @param method
    * @return
    * @deprecated Use isEjbObjectMethod(SerializableMethod method)
    */
   @Deprecated
   protected boolean isEJBObjectMethod(Method method)
   {
      /*
       * Initialize
       */

      // Get the declaring class
      Class<?> declaringClass = method.getDeclaringClass();

      /*
       * Test if declared by EJBObject/EJBLocalObject
       */

      if (declaringClass.getName().equals(EJBObject.class.getName()))
         return true;

      if (declaringClass.getName().equals(EJBLocalObject.class.getName()))
         return true;

      return false;
   }

   /**
    * Determines whether the specified method is an EJB2.x Local
    * or Remote Method
    *
    * @param method
    * @return
    */
   protected boolean isEjbObjectMethod(SerializableMethod method)
   {

      /*
       * Initialize
       */

      // Get the declaring class
      Class<?> declaringClass = null;
      String declaringClassName = method.getDeclaringClassName();
      try
      {
         declaringClass = Class.forName(declaringClassName, false, this.getClassloader());
      }
      catch (ClassNotFoundException e)
      {
         throw new RuntimeException("Invoked Method specifies a declaring class that could not be loaded by the "
               + ClassLoader.class.getSimpleName() + " for EJB " + this.getEjbName());
      }

      /*
       * Test if declared by EJBObject/EJBLocalObject
       */

      if (declaringClass.getName().equals(EJBObject.class.getName()))
         return true;

      if (declaringClass.getName().equals(EJBLocalObject.class.getName()))
         return true;

      // If we've reached here, not EJBObject/EJBLocalObject
      return false;
   }

   /**
    *
    * @param method
    * @return
    * @deprecated Use isHandleMethod(SerializableMethod method)
    */
   @Deprecated
   protected boolean isHandleMethod(Method method)
   {
      if (method.getDeclaringClass().getName().equals(Handle.class.getName()))
         return true;

      return false;
   }

   /**
    * Determines if the specified Method is a Handle Method
    * @param method
    * @return
    */
   protected boolean isHandleMethod(SerializableMethod method)
   {
      // Get the Method
      Method invokingMethod = method.toMethod(this.getClassloader());

      // Use legacy
      return this.isHandleMethod(invokingMethod);
   }

   /**
    * Returns the busines interfaces for this EJB
    * @return
    */
   protected Set<Class<?>> getBusinessInterfaces()
   {
      if (businessInterfaces == null)
      {
         throw new IllegalStateException("Business interfaces not yet initialized");
      }
      return businessInterfaces;
   }

   protected BusinessObjectFactory getBusinessObjectFactory()
   {
      return businessObjectFactory;
   }

   /**
    * Returns true if the passed fully qualified class name, represents a
    * EJB 2.x view of the EJB, corresponding to this container. Else
    * returns false.
    *
    * @param intf Fully qualified class name
    * @return
    */
   private boolean isEjb2xView(String intf)
   {
      String remote = this.getMetaData().getRemote();
      if (remote != null && remote.equals(intf))
      {
         return true;
      }
      String local = this.getMetaData().getLocal();
      if (local != null && local.equals(intf))
      {
         return true;
      }
      return false;
   }

   // ------------------------------------------------------------------------------||
   // Lifecycle Methods ------------------------------------------------------------||
   // ------------------------------------------------------------------------------||

   /**
    * Lifecycle Start
    */
   @Override
   protected void lockedStart() throws Exception
   {
      log.info("Starting " + this);

      // Cache the business interfaces
      final Set<Class<?>> set = new HashSet<Class<?>>();
      final Set<String> businessInterfaceNames = new HashSet<String>();
      if (this.getMetaData().getBusinessLocals() != null)
      {
         businessInterfaceNames.addAll(this.getMetaData().getBusinessLocals());
      }
      if (this.getMetaData().getBusinessRemotes() != null)
      {
         businessInterfaceNames.addAll(this.getMetaData().getBusinessRemotes());
      }
      for (final String businessInterfaceName : businessInterfaceNames)
      {
         final Class<?> businessInterface;
         try
         {
            businessInterface = Class.forName(businessInterfaceName, false, this.getClassloader());
         }
         catch (final ClassNotFoundException cnfe)
         {
            throw new RuntimeException("Could not find marked business interface in this EJB's ClassLoader", cnfe);
         }
         set.add(businessInterface);
      }
      businessInterfaces = Collections.unmodifiableSet(set);

      verifyBusinessObjectFactory();

      super.lockedStart();
   }

   /**
    * Lifecycle Stop
    */
   @Override
   protected void lockedStop() throws Exception
   {
      log.info("Stopping " + this);

      super.lockedStop();
   }

   /**
    * Returns the {@link ExecutorService} used in processing Asynchronous invocations
    */
   public ExecutorService getAsynchronousExecutor()
   {
      return this.asynchronousExecutor;
   }

   public void setBusinessObjectFactory(BusinessObjectFactory factory)
   {
      this.businessObjectFactory = factory;
   }

   protected void verifyBusinessObjectFactory()
   {
      log.warn("EJBTHREE-2126: container " + this + " does not verify the businessObjectFactory");
   }

   public AsyncInvocationMap getCurrentAsyncInvocations()
   {
      return this.currentAsyncInvocations;
   }

   @Inject(bean = "org.jboss.ejb3.async.AsyncInvocationsMap")
   public void setCurrentAsyncInvocations(final AsyncInvocationMap currentAsyncInvocations)
   {
      this.currentAsyncInvocations = currentAsyncInvocations;
   }
}
TOP

Related Classes of org.jboss.ejb3.session.SessionSpecContainer$AsyncInvocationTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.