Package org.jboss.ejb3.async.impl.interceptor

Source Code of org.jboss.ejb3.async.impl.interceptor.AsynchronousClientInterceptor$PojiProxyHack

/*
* JBoss, Home of Professional Open Source.
* Copyright 2009, 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.async.impl.interceptor;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import org.jboss.aop.Dispatcher;
import org.jboss.aop.advice.Interceptor;
import org.jboss.aop.joinpoint.Invocation;
import org.jboss.aop.joinpoint.MethodInvocation;
import org.jboss.aspects.remoting.InvokeRemoteInterceptor;
import org.jboss.aspects.remoting.PojiProxy;
import org.jboss.ejb3.async.impl.AsyncInvocationIdUUIDImpl;
import org.jboss.ejb3.async.impl.ClientExecutorService;
import org.jboss.ejb3.async.impl.util.concurrent.ResultUnwrappingExecutorService;
import org.jboss.ejb3.async.spi.AsyncEndpoint;
import org.jboss.ejb3.async.spi.AsyncInvocation;
import org.jboss.ejb3.async.spi.AsyncInvocationContext;
import org.jboss.ejb3.async.spi.AsyncInvocationId;
import org.jboss.ejb3.async.spi.AsyncInvocationTaskBase;
import org.jboss.ejb3.async.spi.AsyncUtil;
import org.jboss.ejb3.async.spi.CurrentAsyncInvocation;
import org.jboss.logging.Logger;
import org.jboss.metadata.ejb.spec.AsyncMethodsMetaData;
import org.jboss.remoting.InvokerLocator;
import org.jboss.security.SecurityContext;

/**
* Examines invocation metadata to determine if this
* should be handled asynchronously; if so, short-circuits and
* spawns off into a new Thread.
*
* If the invocation has been equipped with an {@link AsyncInvocationContext}
* (ie. is of type {@link AsyncInvocation}), the associated {@link ExecutorService}
* will be used.  Else we'll provide an {@link ExecutorService}
* implementation on behalf of the client.
*
* @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
* @version $Revision: $
*/
public class AsynchronousClientInterceptor implements Interceptor, Serializable
{

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

   /**
    * serialVersionUID
    */
   private static final long serialVersionUID = 1L;

   /**
    * Logger
    */
   private static final Logger log = Logger.getLogger(AsynchronousClientInterceptor.class);

   /*
    * Metadata attachments flagging this invocation's already been dispatched
    */
   private static final String INVOCATION_METADATA_TAG = "ASYNC";

   private static final String INVOCATION_METADATA_ATTR = "BEEN_HERE";

   private static final String INVOCATION_METADATA_VALUE = Boolean.TRUE.toString();

   // --------------------------------------------------------------------------------||
   // Instance Members ---------------------------------------------------------------||
   // --------------------------------------------------------------------------------||

   /**
    * Asynchronous Methods to be handled by this interceptor
    */
   private final AsyncMethodsMetaData asyncMethods;

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

   /**
    * Constructor
    */
   public AsynchronousClientInterceptor(final AsyncMethodsMetaData asyncMethods)
   {
      assert asyncMethods != null : "Async Methods must be supplied";
      this.asyncMethods = asyncMethods;
      log.debug("Created: " + this + " to handle " + asyncMethods);
   }

   // --------------------------------------------------------------------------------||
   // Required Implementations -------------------------------------------------------||
   // --------------------------------------------------------------------------------||

   /**
    * {@inheritDoc}
    * @see org.jboss.aop.advice.Interceptor#getName()
    */
   public String getName()
   {
      return this.getClass().getSimpleName();
   }

   /**
    * {@inheritDocs}
    * @see org.jboss.aop.advice.Interceptor#invoke(org.jboss.aop.joinpoint.Invocation)
    */
   public Object invoke(final Invocation invocation) throws Throwable
   {
      // If asynchronous
      if (this.isAsyncInvocation(invocation))
      {
         // Spawn
         return this.invokeAsync(invocation);
      }
      // Regular synchronous call
      else
      {
         // Continue along the chain
         return invocation.invokeNext();
      }
   }

   // --------------------------------------------------------------------------------||
   // Internal Helper Methods --------------------------------------------------------||
   // --------------------------------------------------------------------------------||

   /**
    * Breaks off the specified invocation into
    * a queue for asynchronous processing, returning
    * a handle to the task
    */
   private Future<?> invokeAsync(final Invocation invocation)
   {
      // Get the appropriate ExecutorService
      final ExecutorService executorService = this.getAsyncExecutor(invocation);

      // Get the existing SecurityContext
      final SecurityContext sc = SecurityActions.getSecurityContext();

      // Copy the invocation (must be done for Thread safety, as we spawn this off and
      // subsequent calls can mess with the internal interceptor index)
      final Invocation nextInvocation = invocation.copy();

      // Mark that we've already been async'd, so when the invocation comes around again we don't infinite loop
      nextInvocation.getMetaData().addMetaData(INVOCATION_METADATA_TAG, INVOCATION_METADATA_ATTR,
            INVOCATION_METADATA_VALUE);

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

      // Make the asynchronous task from the invocation
      final Callable<Object> asyncTask = new AsyncAOPInvocationTask<Object>(nextInvocation, sc, id);

      // Short-circuit the invocation into new Thread
      final Future<Object> task;
      try
      {
         // Mark the thread w/ the UUID so it can be picked up by the ES during submit, and stuffed into the Future
         CurrentAsyncAOPInvocation.markCurrentInvocation(id, nextInvocation);
         task = executorService.submit(asyncTask);
         if (log.isTraceEnabled())
         {
            log.trace("Submitting async invocation " + invocation + " via " + executorService);
         }
      }
      finally
      {
         // Clear the Thread
         CurrentAsyncInvocation.unmarkCurrentInvocationFromThread();
      }

      // Return
      return task;
   }

   /**
    * Determines whether the specified invocation is asynchronous
    * by inspecting its metadata
    *
    * EJB 3.1 4.5.2.2
    */
   private boolean isAsyncInvocation(final Invocation invocation)
   {
      // Precondition check
      if (log.isTraceEnabled())
      {
         log.trace("Checking to see if async: " + invocation);
      }
      assert invocation instanceof MethodInvocation : this.getClass().getName() + " supports only "
            + MethodInvocation.class.getSimpleName() + ", but has been passed: " + invocation;
      final MethodInvocation si = (MethodInvocation) invocation;

      // See if we've already been here, if so, don't handle as async
      final String beenHere = (String) invocation.getMetaData().getMetaData(INVOCATION_METADATA_TAG,
            INVOCATION_METADATA_ATTR);
      if (beenHere != null && beenHere.equals(INVOCATION_METADATA_VALUE))
      {
         // Do not handle
         if (log.isTraceEnabled())
         {
            log.trace("Been here, not dispatching as async again");
         }
         return false;
      }

      // Get the actual method
      final Method actualMethod = si.getActualMethod();

      // Return if the method is async
      return AsyncUtil.methodIsAsynchronous(actualMethod, asyncMethods);
   }

   /**
    * Obtains an appropriate {@link ExecutorService} to handle the invocation
    * based upon the type of {@link Invocation} provided.  If we're got a
    * {@link AsyncInvocation}, the associated {@link ExecutorService} will be used,
    * else we'll supply a default one.
    *
    * @param invocation
    * @return
    */
   private ExecutorService getAsyncExecutor(final Invocation invocation)
   {
      // Precondition checks
      assert invocation != null : "Invocation must be specified";

      // If this invocation has been equipped with an associated ES
      if (invocation instanceof AsyncInvocation)
      {

         // Cast
         final AsyncInvocation asyncInvocation = (AsyncInvocation) invocation;

         // Get out the ES
         final AsyncInvocationContext context = asyncInvocation.getAsyncInvocationContext();
         assert context != null : "async invocation context of " + invocation + " was null";
         final ExecutorService es = context.getAsynchronousExecutor();
         assert es != null : ExecutorService.class.getSimpleName() + " associated with " + context + " was null";
         return es;

      }
      // Supply our own ES for the client
      else
      {
         final InvokerLocator locator = (InvokerLocator) invocation.getMetaData(InvokeRemoteInterceptor.REMOTING,
               InvokeRemoteInterceptor.INVOKER_LOCATOR);
         final Object oid = invocation.getMetaData().getMetaData(Dispatcher.DISPATCHER, Dispatcher.OID);
         final PojiProxy proxy = new PojiProxyHack(oid, locator, new Interceptor[]
         {});
         final AsyncEndpoint container = (AsyncEndpoint) Proxy.newProxyInstance(Thread.currentThread()
               .getContextClassLoader(), new Class<?>[]
         {AsyncEndpoint.class}, proxy);
         return new ResultUnwrappingExecutorService(ClientExecutorService.INSTANCE, container);
      }
   }

   private static final class PojiProxyHack extends PojiProxy
   {
      /**
       * serialVersionUID
       */
      private static final long serialVersionUID = 1L;

      public PojiProxyHack(Object oid, InvokerLocator uri, Interceptor[] interceptors)
      {
         super(oid, uri, interceptors);
      }

      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
      {
         final Object obj = super.invoke(proxy, method, args);
         log.info("Invoked view from proxy: " + method);
         return obj;
      }

   }

   // --------------------------------------------------------------------------------||
   // Inner Classes ------------------------------------------------------------------||
   // --------------------------------------------------------------------------------||

   /**
    * Task to invoke the held invocation in a new Thread, either
    * returning the result or throwing the generated Exception
    */
   private class AsyncAOPInvocationTask<V> extends AsyncInvocationTaskBase<V>
   {
      private final Invocation invocation;

      public AsyncAOPInvocationTask(final Invocation invocation, final SecurityContext sc, final AsyncInvocationId id)
      {
         super(sc, id, SecurityActions.getTccl());
         assert invocation != null : "Invocation must be supplied";
         this.invocation = invocation;
      }

      @Override
      protected void before() throws Exception
      {
         // Mark the current invocation both on the executing Thread and the Invocation
         CurrentAsyncAOPInvocation.markCurrentInvocation(this.id, this.invocation);
      }

      @Override
      @SuppressWarnings("unchecked")
      protected V proceed() throws Throwable
      {
         return (V) invocation.invokeNext();
      }

      @Override
      protected void after() throws Exception
      {
         // Unmark the current invocation both on the executing Thread and the Invocation
         CurrentAsyncAOPInvocation.unmarkCurrentInvocation(this.invocation);
      }

   }

}
TOP

Related Classes of org.jboss.ejb3.async.impl.interceptor.AsynchronousClientInterceptor$PojiProxyHack

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.