Package org.jboss.ejb3.nointerface.impl.invocationhandler

Source Code of org.jboss.ejb3.nointerface.impl.invocationhandler.NoInterfaceViewInvocationHandler

/*
* 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.nointerface.impl.invocationhandler;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.jboss.dependency.spi.ControllerState;
import org.jboss.ejb3.endpoint.Endpoint;
import org.jboss.kernel.spi.dependency.KernelControllerContext;
import org.jboss.logging.Logger;

/**
* NoInterfaceViewInvocationHandler
*
* An {@link InvocationHandler} which corresponds to the
* no-interface view of a EJB container. All calls on the no-interface
* view are routed through this {@link InvocationHandler} to the container.
*
*
* @author Jaikiran Pai
* @version $Revision: $
*/
public class NoInterfaceViewInvocationHandler implements InvocationHandler
{

   /**
    * Equals and hashCode methods are handled within this invocation handler
    */
   private static final Method METHOD_EQUALS;

   private static final Method METHOD_HASH_CODE;

   private static final Method METHOD_TO_STRING;

   static
   {
      try
      {
         METHOD_EQUALS = Object.class.getDeclaredMethod("equals", Object.class);
         METHOD_HASH_CODE = Object.class.getDeclaredMethod("hashCode");
         METHOD_TO_STRING = Object.class.getDeclaredMethod("toString");
      }
      catch (SecurityException e)
      {
         throw new RuntimeException(e);
      }
      catch (NoSuchMethodException e)
      {
         throw new RuntimeException(e);
      }

   }

   /**
    * Logger
    */
   private static Logger logger = Logger.getLogger(NoInterfaceViewInvocationHandler.class);

   /**
    * The KernelControllerContext corresponding to the endpoint for which
    * the no-interface view is to be created by this factory. This context
    * may <i>not</i> be in INSTALLED state. This factory is responsible
    * for pushing it to INSTALLED state whenever necessary.
    *
    * All calls to this invocation handler will be forwarded to the container represented
    * by this context
    *
    *
    */
   private KernelControllerContext endpointContext;

   /**
    * The session used to interact with the {@link Endpoint}
    */
   private Serializable session;

   /**
    * Constructor
    * @param container
    */
   public NoInterfaceViewInvocationHandler(KernelControllerContext endpointContext, Serializable session)
   {
      assert endpointContext != null : "Endpoint context is null for no-interface view invocation handler";
      this.endpointContext = endpointContext;
      this.session = session;
   }

   /**
    * The entry point when a client calls any methods on the no-interface view of a bean,
    * returned through JNDI.
    *
    *
    * @param proxy
    * @param method The invoked method
    * @param args The arguments to the method
    */
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
   {
      // check to see if this method is expected to be handled
      // by the nointerface view (for ex: only public methods of bean are allowed
      // on nointerface view)
      if (!isHandled(method))
      {
         throw new javax.ejb.EJBException("Cannot invoke method " + method.getName() + " on nointerface view");
      }

      // handle equals() and hashCode() in this InvocationHandler
      try
      {
         return handleDirectly(proxy, method, args);
      }
      catch (CannotHandleDirectlyException chde)
      {
         //ignore
         if (logger.isTraceEnabled())
         {
            logger.trace("Cannot handle method: " + method.getName() + " in " + this.getClass().getName());
         }
      }
      // get the endpoint (which will involve pushing it to INSTALLED state)
      Endpoint endpoint = getInstalledEndpoint();
      assert endpoint != null : "No endpoint associated with context " + this.endpointContext
            + " - cannot invoke the method on bean";

      // finally pass-on the control to the endpoint
      return endpoint.invoke(this.session, null, method, args);
   }

   /**
    * Returns the context corresponding to the container, associated with this invocation handler
    *
    * @return
    */
   public KernelControllerContext getContainerContext()
   {
      return this.endpointContext;
   }

   /**
    * Returns the {@link Endpoint} container corresponding to this
    * {@link NoInterfaceViewInvocationHandler}. Internally, the {@link Endpoint}
    * will be first pushed to the INSTALLED state
    *
    * @return
    */
   public Endpoint getInstalledEndpoint()
   {
      if (logger.isTraceEnabled())
      {
         logger.trace("Pushing the endpoint context to INSTALLED state from its current state = "
               + this.endpointContext.getState().getStateString());
      }
      try
      {
         // first push the context corresponding to the endpoint to INSTALLED
         this.endpointContext.getController().change(this.endpointContext, ControllerState.INSTALLED);
         // get hold of the endpoint from its context
         Endpoint endpoint = (Endpoint) this.endpointContext.getTarget();
         return endpoint;
      }
      catch (Throwable t)
      {
         throw new RuntimeException("Error getting endpoint out of container KernelControllerContext "
               + this.endpointContext, t);
      }

   }

   /**
    * Returns the {@link Endpoint} container corresponding to this
    * {@link NoInterfaceViewInvocationHandler}. Note that this method does NOT
    * change the state of the KernelControllerContext of this Endpoint. As such,
    * the Endpoint returned by this method is NOT guaranteed to be in INSTALLED state.
    * If the Endpoint with INSTALLED state is required, then use the {@link #getInstalledEndpoint()}
    * method.
    * 
    * @return
    * @see #getInstalledEndpoint()
    */
   private Endpoint getEndpoint()
   {
      Object endpoint = this.endpointContext.getTarget();
      assert endpoint instanceof Endpoint : "Unexpected type " + endpoint.getClass().getName() + " found in context "
            + this.endpointContext + " Expected " + Endpoint.class.getName();
      return (Endpoint) endpoint;
   }

   /**
    * @see Object#equals(Object)
    */
   @Override
   public boolean equals(Object other)
   {
      // simple object comparison
      if (this == other)
      {
         return true;
      }

      // equals() method contract specifies that if the other object is null
      // then equals() should return false
      if (other == null)
      {
         return false;
      }

      // If the other object is not an instance of MCAwareNoInterfaceViewInvocationHandler
      // then they are not equal
      if (!(other instanceof NoInterfaceViewInvocationHandler))
      {
         return false;
      }

      NoInterfaceViewInvocationHandler otherNoInterfaceViewInvocationHandler = (NoInterfaceViewInvocationHandler) other;

      // First check whether the Endpoints of both these InvocationHandlers are equal. If
      // not, then no need for any further comparison, just return false
      if (!(this.getInstalledEndpoint().equals(otherNoInterfaceViewInvocationHandler.getInstalledEndpoint())))
      {
         return false;
      }

      // If the endpoints are equal, then let's next check whether the sessions for
      // these invocation handlers are equal. If not, return false.
      if (!(this.session.equals(otherNoInterfaceViewInvocationHandler.session)))
      {
         return false;
      }
      // All possible, inequality conditions have been tested, so return true.
      return true;
   }

   /**
    * @see Object#hashCode()
    */
   @Override
   public int hashCode()
   {
      int hashCode = this.getInstalledEndpoint().hashCode();
      if (this.session != null)
      {
         hashCode += this.session.hashCode();
      }
      return hashCode;
   }

   /**
    * @see Object#toString()
    */
   @Override
   public String toString()
   {
      StringBuilder sb = new StringBuilder("No-Interface view for endpoint [ " + this.getEndpoint() + " ]");
      if (this.session != null)
      {
         sb.append(" and session " + this.session);
      }
      return sb.toString();
   }

   /**
    * Handles {@link Object#equals(Object)} and {@link Object#hashCode()} method invocations on the
    * proxy.
    *
    * @param proxy
    * @param method The method that was invoked on the no-interface view
    * @param args The arguments to the method
    * @return 
    * @throws CannotHandleDirectlyException If the <code>method</code> is neither {@link Object#equals(Object) nor Object#hashCode()
    */
   private Object handleDirectly(Object proxy, Method method, Object[] args) throws CannotHandleDirectlyException
   {

      // if equals()
      if (method.equals(METHOD_EQUALS))
      {
         Object other = args[0];
         // if the other object is null, then it's a false straight-away as per the
         // contract of equals method
         if (other == null)
         {
            return false;
         }
         // simple instance comparison
         if (this == other)
         {
            return true;
         }

         // This is the important one (and a good one) - thanks to Carlo
         // When the equals is called on the no-interface view - view1.equals(view2)
         // we somehow have to get the invocation handler (which hold the session information) for view2.
         // So the trick here is to first check whether the other is an instance of
         // MCAwareNoInterfaceViewInvocationHandler. If not, then invoke the equals on that object.
         //  - If the "other" happens to be an no-interface view, the call will be redirected
         // to the invocation handler of view2 and thus we have the session information that we needed
         // from view2.
         //  - If the "other" happens to be an instance of some other class, then that class' equals
         // would return false since its not an instance of MCAwareNoInterfaceViewInvocationHandler.
         if (!(other instanceof NoInterfaceViewInvocationHandler))
         {
            return other.equals(this);
         }

         return this.equals(other);
      }
      // handle hashCode
      else if (method.equals(METHOD_HASH_CODE))
      {
         return this.hashCode();
      }
      else if (method.equals(METHOD_TO_STRING))
      {
         return this.toString();
      }
      throw new CannotHandleDirectlyException();
   }

   /**
    *
    * CannotHandleDirectlyException
    *
    * Will be used to indicate that this {@link NoInterfaceViewInvocationHandler} cannot
    * handle the method invocation in the invocation handler.
    *
    * @author Jaikiran Pai
    * @version $Revision: $
    */
   private class CannotHandleDirectlyException extends Exception
   {
   }

   /**
    *
    * @param m
    * @return
    */
   public boolean isHandled(Method m)
   {
      // We handle only public, non-static, non-final methods
      if (!isPublic(m))
      {
         if (logger.isTraceEnabled())
         {
            logger.trace("Method " + m.getName() + " is *not* public");
         }
         // it's not a public method
         return false;
      }
      if (isFinal(m))
      {
         if (logger.isTraceEnabled())
         {
            logger.trace("Method " + m.getName() + " is final");
         }
         // it's a final method
         return false;
      }
      if (isStatic(m))
      {
         if (logger.isTraceEnabled())
         {
            logger.trace("Method " + m.getName() + " is static");
         }
         // it's a static method
         return false;
      }
      if (isNative(m))
      {
         if (logger.isTraceEnabled())
         {
            logger.trace("Method " + m.getName() + " is native");
         }
         // it's a native method
         return false;
      }
      // we handle rest of the methods
      return true;
   }

   /**
    * Returns true if the {@link Method} <code>m</code> is a public method.
    * Else returns false
    *
    * @param m The method
    * @return
    */
   private boolean isPublic(Method m)
   {
      int modifiers = m.getModifiers();
      if ((Modifier.PUBLIC & modifiers) == Modifier.PUBLIC)
      {
         return true;
      }
      return false;
   }

   /**
    * Returns true if the {@link Method} <code>m</code> is a final method.
    * Else returns false
    *
    * @param m The method
    * @return
    */
   private boolean isFinal(Method m)
   {
      int modifiers = m.getModifiers();
      if ((Modifier.FINAL & modifiers) == Modifier.FINAL)
      {
         return true;
      }
      return false;
   }

   /**
    * Returns true if the {@link Method} <code>m</code> is a static method.
    * Else returns false
    *
    * @param m The method
    * @return
    */
   private boolean isStatic(Method m)
   {
      int modifiers = m.getModifiers();
      if ((Modifier.STATIC & modifiers) == Modifier.STATIC)
      {
         return true;
      }
      return false;
   }

   /**
    * Returns true if the {@link Method} <code>m</code> is a native method.
    * Else returns false
    *
    * @param m The method
    * @return
    */
   private boolean isNative(Method m)
   {
      int modifiers = m.getModifiers();
      if ((Modifier.NATIVE & modifiers) == Modifier.NATIVE)
      {
         return true;
      }
      return false;
   }
}
TOP

Related Classes of org.jboss.ejb3.nointerface.impl.invocationhandler.NoInterfaceViewInvocationHandler

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.