Package com.nokia.dempsy.container.internal

Source Code of com.nokia.dempsy.container.internal.LifecycleHelper$MethodHandle

/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.nokia.dempsy.container.internal;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;

import com.nokia.dempsy.annotations.Activation;
import com.nokia.dempsy.annotations.Evictable;
import com.nokia.dempsy.annotations.MessageHandler;
import com.nokia.dempsy.annotations.MessageKey;
import com.nokia.dempsy.annotations.MessageProcessor;
import com.nokia.dempsy.annotations.Output;
import com.nokia.dempsy.annotations.Passivation;
import com.nokia.dempsy.container.ContainerException;
import com.nokia.dempsy.container.MpContainer;
import com.nokia.dempsy.monitoring.StatsCollector;

/**
* This class holds the MP prototype, and supports invocation of MP methods on an instance.
*/
public class LifecycleHelper
{
   private Object prototype;
   private Class<?> mpClass;
   private String mpClassName;

   private String toStringValue;

   private Method cloneMethod;
   private MethodHandle activationMethod;
   private MethodHandle passivationMethod;
   private Method outputMethod;
   private MethodHandle evictableMethod;
   private AnnotatedMethodInvoker invocationMethods;

   public LifecycleHelper(Object prototype) throws ContainerException
   {
      this.prototype = prototype;
      this.mpClass = prototype.getClass();
      this.mpClassName = mpClass.getName();
      this.toStringValue = getClass().getName() + "[" + mpClassName + "]";

      validateAsMP();
      cloneMethod = introspectClone();

      try
      {
         invocationMethods = new AnnotatedMethodInvoker(mpClass, MessageHandler.class);
      }
      catch(IllegalArgumentException ex)
      {
         throw new ContainerException(ex.getMessage());
      }
      Set<Class<?>> keys = invocationMethods.getMethods().keySet();
      for(Class<?> key: keys)
      {
         Method messageKey = AnnotatedMethodInvoker.introspectAnnotationSingle(key, MessageKey.class);
         activationMethod = new MethodHandle(AnnotatedMethodInvoker.introspectAnnotationSingle(mpClass, Activation.class),
               (messageKey == null)?null:messageKey.getReturnType());
      }
      passivationMethod = new MethodHandle(AnnotatedMethodInvoker.introspectAnnotationSingle(mpClass, Passivation.class));
      outputMethod = AnnotatedMethodInvoker.introspectAnnotationSingle(mpClass, Output.class);
      evictableMethod = new MethodHandle(AnnotatedMethodInvoker.introspectAnnotationSingle(mpClass, Evictable.class));
   }

   /**
    * Creates a new instance from the prototype.
    */
   public Object newInstance() throws InvocationTargetException, IllegalAccessException
   {
      return cloneMethod.invoke(prototype);
   }

   /**
    * Invokes the activation method of the passed instance.
    */
   public boolean activate(Object instance, Object key, byte[] activationData) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
   {
      Object o = activationMethod.invoke(instance, key, activationData);
      return o == null ? true : ((Boolean)o);
   }
  
   /**
    * Returns true if the activation method on this prototype exists and the provided
    * exception can be assigned to one of it's declared checked exceptions.
    */
   public boolean activateCanThrowChecked(Throwable th) { return activationMethod.canThrowCheckedException(th); }

   /**
    * Invokes the passivation method of the passed instance. Will return the object's passivation data, <code>null</code> if there is none.
    *
    * @throws InvocationTargetException
    * @throws IllegalAccessException
    * @throws IllegalArgumentException
    */
   public byte[] passivate(Object instance) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
   {
      return (byte[])passivationMethod.invoke(instance);
   }

   /**
    * Invokes the appropriate message handler of the passed instance. Caller is responsible for not passing <code>null</code> messages.
    *
    * @throws ContainerException
    * @throws InvocationTargetException
    * @throws IllegalAccessException
    * @throws IllegalArgumentException
    */
   public Object invoke(Object instance, Object message, StatsCollector statsCollector) throws ContainerException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
   {
      Class<?> messageClass = message.getClass();
      if(!isMessageSupported(message))
         throw new ContainerException(mpClassName + ": no handler for messages of type: " + messageClass.getName());

      StatsCollector.TimerContext tctx = null;
      try
      {
         tctx = statsCollector.handleMessageStarted();
         return invocationMethods.invokeMethod(instance, message);
      }
      finally
      {
         if (tctx != null) tctx.stop();
      }
   }

   public String invokeDescription(MpContainer.Operation op, Object message)
   {
      Method method = null;
      Class<?> messageClass = void.class;
      if(op == MpContainer.Operation.output)
         method = outputMethod;
      else
      {
         if(message == null)
            return toStringValue + ".?(null)";

         messageClass = message.getClass();
         if(!isMessageSupported(message))
            return toStringValue + ".?(" + messageClass.getName() + ")";

         method = invocationMethods.getMethodForClass(message.getClass());
      }

      return toStringValue + "." + (method == null ? "?" : method.getName()) + "(" + messageClass.getSimpleName() + ")";
   }

   public Object getPrototype()
   {
      return prototype;
   }

   /**
    * Invokes the output method, if it exists. If the instance does not have an annotated output method, this is a no-op (this is simpler than requiring the
    * caller to check every instance).
    *
    * @throws InvocationTargetException
    * @throws IllegalAccessException
    * @throws IllegalArgumentException
    */
   public Object invokeOutput(Object instance) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
   {
      return (outputMethod != null) ? outputMethod.invoke(instance) : null;
   }
  
   /**
    * Invokes the evictable method on the provided instance.
    * If the evictable is not implemented, returns false.
    *
    * @param instance
    * @return
    * @throws IllegalArgumentException
    * @throws IllegalAccessException
    * @throws InvocationTargetException
    */
   public boolean invokeEvictable(Object instance) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
   {
      return isEvictableSupported() ? (Boolean)evictableMethod.invoke(instance): false;
   }

   /**
    * Determines whether the passed class matches the prototype's class.
    */
   public boolean isMatchingClass(Class<?> klass)
   {
      return klass.equals(prototype.getClass());
   }

   /**
    * Determines whether this MP has a handler for the passed message. Will walk the message's class hierarchy if there is not an exact match.
    */
   public boolean isMessageSupported(Object message)
   {
      return invocationMethods.isValueSupported(message);
   }

   /**
    * Determines whether this MP provides an output method.
    */
   public boolean isOutputSupported()
   {
      return outputMethod != null;
   }
  
   /**
    * Determines whether this MP provides an evictable method.
    */
   public boolean isEvictableSupported()
   {
      return evictableMethod.getMethod() != null;
   }

   /**
    * To instances are equal if they wrap prototypes of the same class.
    */
   @Override
   public final boolean equals(Object obj)
   {
      if(this == obj)
         return true;
      else if(obj instanceof LifecycleHelper)
      {
         LifecycleHelper that = (LifecycleHelper)obj;
         return this.prototype.getClass() == that.prototype.getClass();
      }
      else
         return false;
   }

   @Override
   public final int hashCode()
   {
      return prototype.getClass().hashCode();
   }

   @Override
   public String toString()
   {
      return toStringValue;
   }

   //----------------------------------------------------------------------------
   //  Internals
   //----------------------------------------------------------------------------

   private void validateAsMP() throws ContainerException
   {
      if(mpClass.getAnnotation(MessageProcessor.class) == null)
         throw new ContainerException("MP class not annotated as MessageProcessor: " + mpClassName);
   }

   private Method introspectClone() throws ContainerException
   {
      try
      {
         // we do *NOT* allow inherited implementation
         return mpClass.getDeclaredMethod("clone");
      }
      catch(SecurityException e)
      {
         throw new ContainerException("container does not have access to the message processor class \"" + mpClassName + "\"", e);
      }
      catch(NoSuchMethodException e)
      {
         throw new ContainerException("The message processor class \"" + mpClassName + "\" does not declare the clone() method.");
      }
   }
  
   /**
    * Class to handle method calls for activation and passivation
    *
    */
   protected class MethodHandle
   {
      private Method method;
      private int keyPosition=-1;
      private int binayPosition=-1;
      private int totalArguments=0;

      public MethodHandle(Method method)
      {
         this(method, null);
      }

      public MethodHandle(Method method, Class<?> keyClass)
      {
         this.method = method;
         if(this.method != null)
         {
            Class<?>[] parameterTypes = method.getParameterTypes();
            this.totalArguments = parameterTypes.length;
            for(int i = 0; i < parameterTypes.length; i++)
            {
               Class<?> parameter = parameterTypes[i];
               if(parameter.isArray() && parameter.getComponentType().isAssignableFrom(byte.class))
               {
                  this.binayPosition = i;
               }
               else if(keyClass != null && parameter.isAssignableFrom(keyClass))
               {
                  this.keyPosition = i;
               }
            }
         }
      }
     
      public Object invoke(Object instance, Object key, byte[] data) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
      {
         if(this.method != null)
         {
            Object[] parameters = new Object[this.totalArguments];
            if(this.keyPosition>-1) parameters[this.keyPosition]=key;
            if(this.binayPosition>-1)parameters[this.binayPosition]=data;
            return this.method.invoke(instance, parameters);
         }
         return null;
      }
      public Object invoke(Object instance) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
      {
         return this.invoke(instance, null, null);
      }
     
      public Method getMethod()
      {
         return this.method;
      }
     
      public boolean canThrowCheckedException(Throwable th)
      {
         if (method == null || th == null)
            return false;
        
         for (Class<?> cur : method.getExceptionTypes())
            if (cur.isInstance(th))
               return true;
         return false;
      }
   }
}
TOP

Related Classes of com.nokia.dempsy.container.internal.LifecycleHelper$MethodHandle

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.