Package org.jboss.ejb3.proxy.impl.handler.session

Source Code of org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandlerBase

/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, 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.proxy.impl.handler.session;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashSet;
import java.util.Set;

import org.jboss.aop.advice.Interceptor;
import org.jboss.ejb3.common.lang.SerializableMethod;
import org.jboss.ejb3.proxy.spi.container.InvokableContext;
import org.jboss.logging.Logger;

/**
* SessionProxyInvocationHandlerBase
*
* Abstract base from which all JBoss Session Proxy InvocationHandlers
* may extend
*
* @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
* @version $Revision: $
*/
public abstract class SessionProxyInvocationHandlerBase implements SessionProxyInvocationHandler, Serializable
{

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

   private static final long serialVersionUID = 1L;

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

   /*
    * Method Names
    */
   private static final String METHOD_NAME_TO_STRING = "toString";

   private static final String METHOD_NAME_EQUALS = "equals";

   private static final String METHOD_NAME_HASH_CODE = "hashCode";

   /*
    * Local Methods
    */
   private static final Method METHOD_TO_STRING;

   private static final Method METHOD_EQUALS;

   private static final Method METHOD_HASH_CODE;

   static
   {
      try
      {
         METHOD_TO_STRING = Object.class.getMethod(METHOD_NAME_TO_STRING);
         METHOD_EQUALS = Object.class.getMethod(METHOD_NAME_EQUALS, Object.class);
         METHOD_HASH_CODE = Object.class.getMethod(METHOD_NAME_HASH_CODE);
      }
      catch (NoSuchMethodException nsme)
      {
         throw new RuntimeException(
               "Methods for handling directly by the InvocationHandler were not initialized correctly", nsme);
      }

   }

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

   /**
    * The target for this Invocation (for instance, Session ID)
    */
   private Object target;

   /**
    * The interceptors to apply to inovcations upon this handler
    */
   private Interceptor[] interceptors;

   /**
    * The name under which the target container is registered
    */
   private String containerName;

   /**
    * The Globally-unique Container ID
    */
   private String containerGuid;

   /**
    * Fully-qualified name of the class targeted either for injection
    * or casting to support getInvokedBusinessInterface.  May be
    * null to denote non-deterministic invocation
    */
   private String businessInterfaceType;

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

   /**
    * Constructor
    *
    * @param containerName The name of the target Container
    * @param containerGuid The globally-unique name of the container
    * @param interceptors The interceptors to apply to invocations upon this handler
    * @param businessInterfaceType Possibly null FQN of business interface
    * @param target The target object (Session ID)
    */
   protected SessionProxyInvocationHandlerBase(final String containerName, final String containerGuid,
         final Interceptor[] interceptors, final String businessInterfaceType, final Object target)
   {
      this.setContainerName(containerName);
      this.setContainerGuid(containerGuid);
      this.setInterceptors(interceptors);
      this.setBusinessInterfaceType(businessInterfaceType);
      this.setTarget(target);
   }

   // ------------------------------------------------------------------------------||
   // Functional Methods -----------------------------------------------------------||
   // ------------------------------------------------------------------------------||

   /**
    * {@inheritDoc}
    * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
    */
   public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable
   {
      // Obtain an explicitly-specified actual class
      final String actualClass = this.getBusinessInterfaceType();

      // If we can service this invocation in the handler itself
      if (this.canBeHandledDirectly(method))
      {
         // Handle right here
         return this.handleInvocationDirectly(proxy, args, method);
      }

      // Adjust args if null to empty array
      Object[] usedArgs = args;
      if (usedArgs == null)
      {
         usedArgs = new Object[]
         {};
      }

      // Set the invoked method
      final SerializableMethod invokedMethod = new SerializableMethod(method, actualClass);
     
      /*
       * Obtain the Container
       */
      final InvokableContext container = this.getContainer(invokedMethod.toMethod(), usedArgs);

      /*
       * Invoke
       */

      // Invoke
      final Object result = container.invoke(proxy, invokedMethod, usedArgs);

      // Return
      return result;
   }

   /**
    * Handles the current invocation directly in this invocation handler.  Only
    * a subset of method invocations are eligible for this treatment, else
    * a NotEligibleForDirectInvocationException will be thrown
    *
    * @param proxy
    * @param args Arguments of the current invocation
    * @param invokedMethod The method invoked
    * @return
    * @throws NotEligibleForDirectInvocationException
    */
   protected Object handleInvocationDirectly(Object proxy, Object[] args, Method invokedMethod)
         throws NotEligibleForDirectInvocationException
   {
      // Obtain the invoked method
      assert invokedMethod != null : "Invoked Method was not set upon invocation of " + this.getClass().getName();

      // equals
      if (invokedMethod.equals(METHOD_EQUALS))
      {
         assert args.length == 1 : "Invocation for 'equals' should have exactly one argument, instead was: "
               + invokedMethod;
         Object argument = args[0];
         return this.invokeEquals(proxy, argument);
      }

      // toString
      if (invokedMethod.equals(METHOD_TO_STRING))
      {
         // Perform assertions
         assert Proxy.isProxyClass(proxy.getClass()) : "Specified proxy invoked is not of type "
               + Proxy.class.getName();

         // Obtain implemented interfaces
         Class<?>[] implementedInterfaces = proxy.getClass().getInterfaces();
         Set<Class<?>> interfacesSet = new HashSet<Class<?>>();
         for (Class<?> interfaze : implementedInterfaces)
         {
            interfacesSet.add(interfaze);
         }

         // Construct a return value
         StringBuffer sb = new StringBuffer();
         sb.append("Proxy to ");
         sb.append(this.getContainerName());
         sb.append(" implementing ");
         sb.append(interfacesSet);

         // Return
         return sb.toString();
      }
      // hashCode
      if (invokedMethod.equals(METHOD_HASH_CODE))
      {
         return this.invokeHashCode(proxy);
      }

      // If no eligible methods were invoked
      throw new NotEligibleForDirectInvocationException("Current invocation \"" + invokedMethod
            + "\" is not eligible for direct handling by " + this);
   }

   /**
    * Returns whether or not this handler is capable of handling the invoked
    * method request directly, without passing along to the container
    * @param invokedMethod The invoked Method
    * @return
    */
   protected boolean canBeHandledDirectly(final Method invokedMethod)
   {
      // We handle: equals, hashCode, toString
      return (invokedMethod.equals(METHOD_EQUALS) || invokedMethod.equals(METHOD_TO_STRING) || invokedMethod
            .equals(METHOD_HASH_CODE));
   }

   /**
    * Handles invocation of "equals(Object)" upon a Session Proxy
    *
    * EJB 3.0 Specification 3.4.5.1, 3.4.5.2
    *
    * @param proxy
    * @param args
    * @return
    */
   protected boolean invokeEquals(final Object proxy, final Object argument)
   {
      /*
       * EJB 3.0 Core Specification 3.4.5.1:
       *
       * A stateful session object has a unique identity that is assigned by the
       * container at the time the object is created. A client of the stateful
       * session bean business interface can determine if two business interface
       * references refer to the same session object by use of the equals method.
       *
       * All stateful session bean references to the same business interface for
       * the same stateful session bean instance will be equal. Stateful session
       * bean references to different interface types or to different session bean
       * instances will not have the same identity.
       */

      /*
       * EJB 3.0 Specification 3.4.5.2:
       *
       * All business object references of the same interface type for the same
       * stateless session bean have the same object identity, which is
       * assigned by the container.
       *
       * The equals method always returns true when used to compare references
       * to the same business interface type of the same session bean.
       *
       * Session bean references to either different business interface types
       * or different session beans will not be equal."
       */

      // Short-circuit if we're given null
      // EJBTHREE-2039
      if (argument == null)
      {
         return false;
      }

      // object identity
      if (proxy == argument)
      {
         return true;
      }

      // Ensure we've got j.l.r.Proxies
      if (!Proxy.isProxyClass(proxy.getClass()) || !Proxy.isProxyClass(argument.getClass()))
      {
         if (log.isTraceEnabled())
         {
            log.trace("Not equal; Either " + proxy + " or " + argument + " is not a " + Proxy.class.getName() + " type");
         }
         return false;
      }
     

      // Get the InvocationHandlers
      InvocationHandler proxyHandler = Proxy.getInvocationHandler(proxy);
      InvocationHandler argumentHandler = Proxy.getInvocationHandler(argument);

      // If argument handler is not a SessionProxyInvocationHandler
      if (!(argumentHandler instanceof SessionProxyInvocationHandler))
      {
         if (log.isTraceEnabled())
         {
            log.trace(argument + " and " + this + " are not equal; handler of argument is not a "
                  + SessionProxyInvocationHandler.class.getName());
         }
         return false;
      }

      // Cast
      SessionProxyInvocationHandler proxySHandler = (SessionProxyInvocationHandler) proxyHandler;
      SessionProxyInvocationHandler argumentSHandler = (SessionProxyInvocationHandler) argumentHandler;
     
      // Ensure the business interface types are equal
      String proxySBusinessInterface = proxySHandler.getBusinessInterfaceType();
      String argumentSBusinessInterface = argumentSHandler.getBusinessInterfaceType();
      if (proxySBusinessInterface == null || !proxySBusinessInterface.equals(argumentSBusinessInterface))
      {
         if (log.isTraceEnabled())
         {
            log.trace("Business interface type of proxy: " + proxySBusinessInterface
                  + " and business interface type of argument " + argumentSBusinessInterface + " are not equal");
         }
         return false;
      }
     
     
      // Ensure target containers are equal
      String proxyContainerName = proxySHandler.getContainerName();
      assert proxyContainerName != null : "Container Name for " + proxySHandler + " was not set and is required";
      if (!proxyContainerName.equals(argumentSHandler.getContainerName()))
      {
         return false;
      }

      // If target (Session ID) is specified, ensure equal
      Object proxyTarget = proxySHandler.getTarget();
      if (proxyTarget != null)
      {
         Object argumentTarget = argumentSHandler.getTarget();
         if (!proxyTarget.equals(argumentTarget))
         {
            if (log.isTraceEnabled())
            {
               log.trace("Not equal; different Session IDs - proxy== " + proxyTarget + ", argument==" + argumentTarget);
            }
            return false;
         }
      }

      // All conditions passed, so true
      return true;

   }

   /**
    * Handles invocation of "hashCode()" upon the proxy
    *
    * @param proxy
    * @return
    */
   protected int invokeHashCode(Object proxy)
   {
      // Get the InvocationHandler
      assert Proxy.isProxyClass(proxy.getClass()) : "Unexpected proxy implementation";
      InvocationHandler handler = Proxy.getInvocationHandler(proxy);
      assert handler instanceof SessionProxyInvocationHandler;
      SessionProxyInvocationHandler sHandler = (SessionProxyInvocationHandler) handler;

      int hash = 0;

      // Generate unique String by value according to rules in "invokeEquals";
      // Destination Container
      StringBuffer sb = new StringBuffer();
      sb.append(sHandler.getContainerName());
      sb.append(sHandler.getContainerGuid());
      String compositionString = sb.toString();

      // Make the hash
      hash = compositionString.hashCode();

      // If there's a target in play, take that into account
      Object target = sHandler.getTarget();
      if (target != null)
      {
         hash += target.hashCode();
      }

      // Return the hash
      return hash;
   }

   // ------------------------------------------------------------------------------||
   // Contracts --------------------------------------------------------------------||
   // ------------------------------------------------------------------------------||

   /**
    * Obtains the Container upon which this Proxy should invoke
    *
    * @return
    */
   protected abstract InvokableContext getContainer(final Method method, final Object[] args);

   // ------------------------------------------------------------------------------||
   // Accessors / Mutators ---------------------------------------------------------||
   // ------------------------------------------------------------------------------||

   public Object getTarget()
   {
      return target;
   }

   public void setTarget(final Object target)
   {
      this.target = target;
   }

   public String getContainerName()
   {
      return containerName;
   }

   public void setContainerName(final String containerName)
   {
      assert containerName != null && containerName.trim().length() > 0 : "Container Name must be specified";
      this.containerName = containerName;
   }

   public Interceptor[] getInterceptors()
   {
      return interceptors;
   }

   public void setInterceptors(final Interceptor[] interceptors)
   {
      this.interceptors = interceptors;
   }

   public String getContainerGuid()
   {
      return containerGuid;
   }

   public void setContainerGuid(final String containerGuid)
   {
      this.containerGuid = containerGuid;
   }

   public String getBusinessInterfaceType()
   {
      return businessInterfaceType;
   }

   public void setBusinessInterfaceType(String businessInterfaceType)
   {
      this.businessInterfaceType = businessInterfaceType;
   }

}
TOP

Related Classes of org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandlerBase

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.