Package org.jboss.soa.esb.listeners.message

Source Code of org.jboss.soa.esb.listeners.message.BeanContainerAction$AttachmentResolver

/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt 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.soa.esb.listeners.message;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.log4j.Logger;
import org.jboss.internal.soa.esb.assertion.AssertArgument;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.actions.AbstractActionPipelineProcessor;
import org.jboss.soa.esb.actions.ActionLifecycleException;
import org.jboss.soa.esb.actions.ActionProcessingException;
import org.jboss.soa.esb.actions.annotation.AttachmentParam;
import org.jboss.soa.esb.actions.annotation.BodyParam;
import org.jboss.soa.esb.actions.annotation.OnException;
import org.jboss.soa.esb.actions.annotation.OnSuccess;
import org.jboss.soa.esb.actions.annotation.Process;
import org.jboss.soa.esb.actions.annotation.PropertyParam;
import org.jboss.soa.esb.configure.AnnotationUtil;
import org.jboss.soa.esb.configure.Configurator;
import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.listeners.ListenerTagNames;
import org.jboss.soa.esb.message.Attachment;
import org.jboss.soa.esb.message.Body;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.message.MessagePayloadProxy;
import org.jboss.soa.esb.message.Properties;

/**
* Bean Action Container.
* <p/>
* Container Action class for @ProcessMethod annotated bean action.
*
* @author <a href="mailto:tom.fennelly@jboss.com">tom.fennelly@jboss.com</a>
*/
public class BeanContainerAction extends AbstractActionPipelineProcessor {

  private final static Logger logger = Logger.getLogger(BeanContainerAction.class);

  private String actionName;
  private Object bean;
  private ConfigTree configTree;
    private MessagePayloadProxy payloadProxy;
  private Method processMethod;
  private ParamResolver[] paramResolvers;
  private Map<String, Method> onSuccessMethods;
  private Map<String, Method> onExceptionMethods;

  public BeanContainerAction(Object bean, ConfigTree configTree) throws ConfigurationException {
    AssertArgument.isNotNull(bean, "bean");
    AssertArgument.isNotNull(configTree, "configTree");

    actionName = configTree.getAttribute(ListenerTagNames.ACTION_ELEMENT_TAG);
   
    this.bean = bean;
    this.configTree = configTree;

    payloadProxy = new MessagePayloadProxy(configTree);
   
    Map<String, Method> processMethods = getAnnotatedMethods(bean.getClass(), Process.class);
    if(processMethods.isEmpty()) {
      throw new IllegalArgumentException("Invalid Bean Action type '" + bean.getClass().getName() + "'. An Action bean must contain at least one public method annotated with the @ProcessMethod annotation.");
    }
   
    if(processMethods.size() == 1) {
      processMethod = processMethods.values().iterator().next();
    } else {
      String processMethodName = configTree.getAttribute(ListenerTagNames.PROCESS_METHOD_TAG);
     
      if(processMethodName == null) {
        throw new ConfigurationException("Invalid configuration for Bean Action '" + actionName + "' (bean type'" + bean.getClass().getName() + "'). Bean contain 1+ public methods annotated with the @ProcessMethod annotation.  The <action> '" + ListenerTagNames.PROCESS_METHOD_TAG + "' attribute must be specified.");
      } else if (!processMethods.containsKey(processMethodName)) {
        throw new ConfigurationException("Invalid configuration for Bean Action '" + actionName + "' (bean type'" + bean.getClass().getName() + "'). Bean does not contain a public method named '" + processMethodName + "', annotated with the @ProcessMethod annotation.");
      }
     
      processMethod = processMethods.get(processMethodName);
    }
   
    Class<?>[] args = processMethod.getParameterTypes();
    Annotation[][] paramAnnotations = processMethod.getParameterAnnotations();
   
    paramResolvers = new ParamResolver[args.length];
    for(int i = 0; i < args.length; i++) {
      BodyParam bodyParam = findAnnotation(BodyParam.class, paramAnnotations[i]);
      PropertyParam propertyParam = findAnnotation(PropertyParam.class, paramAnnotations[i]);
      AttachmentParam attachmentParam = findAnnotation(AttachmentParam.class, paramAnnotations[i]);
      int annotationCount = 0;
     
      if(bodyParam != null) {
        annotationCount++;
      }
      if(propertyParam != null) {
        annotationCount++;
      }
      if(attachmentParam != null) {
        annotationCount++;
      }
     
      if(annotationCount == 0) {
        paramResolvers[i] = new CascadingParamResolver(args[i]);
      } else if(annotationCount == 1) {
        if(bodyParam != null) {
          paramResolvers[i] = new BodyResolver(args[i], bodyParam);
        } else if(propertyParam != null) {
          paramResolvers[i] = new PropertyResolver(args[i], propertyParam);
        } else if(attachmentParam != null) {
          paramResolvers[i] = new AttachmentResolver(args[i], attachmentParam);
        }
      } else {
        throw new IllegalArgumentException("Invalid Bean Action type '" + bean.getClass().getName() + "'. The @ProcessMethod annotated method '" + processMethod.getName() + "' contains an argument that conflicting ParamResolver annotation.");       
      }
    }
   
    onSuccessMethods = getAnnotatedMethods(bean.getClass(), OnSuccess.class);
    onExceptionMethods = getAnnotatedMethods(bean.getClass(), OnException.class);
   
    Configurator.configure(bean, configTree);
  }

  private <T extends Annotation> T findAnnotation(Class<T> annotation, Annotation[] annotations) {
    for(Annotation annotationEntry : annotations) {
      if(annotation.isInstance(annotationEntry)) {
        return annotation.cast(annotationEntry);
      }
    }
    return null;
  }

  /* (non-Javadoc)
   * @see org.jboss.soa.esb.actions.AbstractActionLifecycle#initialise()
   */
  @Override
  public void initialise() throws ActionLifecycleException {
    Configurator.initialise(bean, configTree);
  }

  /* (non-Javadoc)
   * @see org.jboss.soa.esb.actions.ActionPipelineProcessor#process(org.jboss.soa.esb.message.Message)
   */
  public Message process(Message message) throws ActionProcessingException {
    Object processResult = null;
   
    try {
      Object[] params = new Object[paramResolvers.length];
     
      for(int i = 0; i < paramResolvers.length; i++) {
        try {
          params[i] = paramResolvers[i].getParam(message);
        } catch (MessageDeliverException e) {
          throw new ActionProcessingException("Error resolving method parameter from ESB message.", e);
        }
      }
     
      processResult = processMethod.invoke(bean, params);           
    } catch (IllegalArgumentException e) {
      throw new ActionProcessingException("Bean Action '" + actionName + "' exception.", e);
    } catch (IllegalAccessException e) {
      throw new ActionProcessingException("Bean Action '" + actionName + "' exception.", e);
    } catch (InvocationTargetException e) {
      Throwable targetException = e.getTargetException();
      if(targetException instanceof ActionProcessingException) {
        throw (ActionProcessingException) targetException;
      } else if (targetException instanceof RuntimeException) {
        throw (RuntimeException) targetException;
      } else {
        throw new ActionProcessingException("Bean Action '" + actionName + "' exception.", targetException);
      }
    }
   
    if(processResult == null && processMethod.getReturnType() != void.class) {
      // Terminate the pipeline...
      return null;
    }
   
    if(processResult instanceof Message) {
      return (Message) processResult;
    } else if(processResult != null) {
      try {
        payloadProxy.setPayload(message, processResult);
      } catch (MessageDeliverException e) {
        throw new ActionProcessingException("Error injecting 'out' payload into ESB message.", e);
      }
    }
   
    return message;
  }

  /* (non-Javadoc)
   * @see org.jboss.soa.esb.actions.AbstractActionPipelineProcessor#processSuccess(org.jboss.soa.esb.message.Message)
   */
  @Override
  public void processSuccess(Message message) {
    if(!onSuccessMethods.isEmpty()) {
      for(Method method : onSuccessMethods.values()) {
        Class<?>[] args = method.getParameterTypes();
        try {
          if(args.length == 0) {
            method.invoke(bean, new Object[] {});
          } else if(args.length == 1 && args[0] == Message.class) {
            method.invoke(bean, new Object[] {message});           
          } else {
            logger.debug("@OnSuccessMethod '"+ method.getName() + "' has too many arguments, or contains the wrong combination of args [" + Arrays.asList(args) + "].");
          }
        } catch (IllegalArgumentException e) {
          logger.error("Exception while invoking @OnSuccessMethod '"+ method.getName() + "'.", e);
        } catch (IllegalAccessException e) {
          logger.error("Exception while invoking @OnSuccessMethod '"+ method.getName() + "'.", e);
        } catch (InvocationTargetException e) {
          logger.error("Exception while invoking @OnSuccessMethod '"+ method.getName() + "'.", e.getTargetException());
        }
      }
    }
  }
 
  /* (non-Javadoc)
   * @see org.jboss.soa.esb.actions.AbstractActionPipelineProcessor#processException(org.jboss.soa.esb.message.Message, java.lang.Throwable)
   */
  @Override
  public void processException(Message message, Throwable th) {
    if(!onExceptionMethods.isEmpty()) {
      for(Method method : onExceptionMethods.values()) {
        Class<?>[] args = method.getParameterTypes();
        try {
          if(args.length == 0) {
            method.invoke(bean, new Object[] {});
          } else if(args.length == 1 && args[0] == Message.class) {
            method.invoke(bean, new Object[] {message});           
          } else if(args.length == 2 && args[0] == Message.class && args[1] == Throwable.class) {
            method.invoke(bean, new Object[] {message, th});           
          } else {
            logger.debug("@OnExceptionMethod '"+ method.getName() + "' has too many arguments, or contains the wrong combination of args [" + Arrays.asList(args) + "].");
          }
        } catch (IllegalArgumentException e) {
          logger.error("Exception while invoking @OnExceptionMethod '"+ method.getName() + "'.", e);
        } catch (IllegalAccessException e) {
          logger.error("Exception while invoking @OnExceptionMethod '"+ method.getName() + "'.", e);
        } catch (InvocationTargetException e) {
          logger.error("Exception while invoking @OnExceptionMethod '"+ method.getName() + "'.", e.getTargetException());
        }
      }
    }
  }

  /* (non-Javadoc)
   * @see org.jboss.soa.esb.actions.AbstractActionLifecycle#destroy()
   */
  @Override
  public void destroy() throws ActionLifecycleException {
    Configurator.destroy(bean);
  }
 
  public static boolean isAnnotatedActionClass(Class<?> runtimeClass) {
    // It's an Action bean if it has one or more @ProcessMethod annotated methods...
    return (!getAnnotatedMethods(runtimeClass, Process.class).isEmpty());
  }

  private static Map<String, Method> getAnnotatedMethods(Class<?> runtimeClass, Class<? extends Annotation> annotation) {
    AssertArgument.isNotNull(runtimeClass, "runtimeClass");

    Method[] publicMethods = runtimeClass.getMethods();
    Map<String, Method> processMethods = new LinkedHashMap<String, Method>();
   
    for(Method method : publicMethods) {
      if(method.isAnnotationPresent(annotation)) {
        processMethods.put(method.getName(), method);
      }
    }
   
    return processMethods;
  }
 
  private interface ParamResolver {
    Object getParam(Message message) throws MessageDeliverException;
  }
 
  private class CascadingParamResolver implements ParamResolver {
   
    private Class<?> paramType;
    private BodyResolver bodyResolver;
    private PropertyResolver propertyResolver;
    private AttachmentResolver attachmentResolver;
   
    private CascadingParamResolver(Class<?> paramType) {
      this.paramType = paramType;
      bodyResolver = new BodyResolver(paramType, null);
      propertyResolver = new PropertyResolver(paramType, null);
      attachmentResolver = new AttachmentResolver(paramType, null);
    }

    public Object getParam(Message message) throws MessageDeliverException {
      if(Message.class.isAssignableFrom(paramType)) {
        return message;
      }
     
      Object payload = payloadProxy.getPayload(message);
     
      if(paramType.isInstance(payload)) {
        return payload;
      }

      // Now lets cascade, trying the different message parts to find the first match....
      Object param = bodyResolver.getParam(message);
      if(param == null) {
        param = propertyResolver.getParam(message);
        if(param == null) {
          param = attachmentResolver.getParam(message);
        }
      }
     
      return param;
    }   
  }
 
  private class PropertyResolver implements ParamResolver {
   
    private Class<?> paramType;
    private PropertyParam property;
   
    private PropertyResolver(Class<?> paramType, PropertyParam property) {
      this.paramType = paramType;
      this.property = property;
    }

    public Object getParam(Message message) throws MessageDeliverException {
      Properties properties = message.getProperties();
     
      String propertyName = AnnotationUtil.getPropertyName(property);
      if(propertyName != null) {
        Object param = properties.getProperty(propertyName);
        if(param != null && !paramType.isInstance(param)) {
          throw new MessageDeliverException("Named property '" + propertyName + "' exists on ESB message but is not of type '" + paramType.getName() + "'.");
        }
        return param;
      } else {
        for(String propertyNameOnMessage : properties.getNames()) {
          Object param = properties.getProperty(propertyNameOnMessage);
          if(param != null && paramType.isInstance(param)) {
            return param;
          }
        }
      }

      return null;
    }   
  }
 
  private class BodyResolver implements ParamResolver {
   
    private Class<?> paramType;
    private BodyParam bodyParam;
   
    private BodyResolver(Class<?> paramType, BodyParam bodyParam) {
      this.paramType = paramType;
      this.bodyParam = bodyParam;
    }

    public Object getParam(Message message) throws MessageDeliverException {
      Body body = message.getBody();
     
      String bodyPartName = AnnotationUtil.getBodyName(bodyParam);
      if(bodyPartName != null) {
        Object param = body.get(bodyPartName);
        if(param != null && !paramType.isInstance(param)) {
          throw new MessageDeliverException("Named Body part '" + bodyPartName + "' exists on ESB message but is not of type '" + paramType.getName() + "'.");
        }
        return param;
      } else {     
        for(String bodyPartNameOnMessage : body.getNames()) {
          Object param = body.get(bodyPartNameOnMessage);
          if(param != null && paramType.isInstance(param)) {
            return param;
          }
        }
      }

      return null;
    }   
  }
 
  private class AttachmentResolver implements ParamResolver {
   
    private Class<?> paramType;
    private AttachmentParam attachmentParam;
   
    private AttachmentResolver(Class<?> paramType, AttachmentParam attachmentParam) {
      this.paramType = paramType;
      this.attachmentParam = attachmentParam;
    }

    public Object getParam(Message message) throws MessageDeliverException {
      Attachment attachment = message.getAttachment();
     
      String attachPartName = AnnotationUtil.getAttachmentName(attachmentParam);
      if(attachPartName != null) {
        Object param = attachment.get(attachPartName);
        if(param != null && !paramType.isInstance(param)) {
          throw new MessageDeliverException("Named Attachment part '" + attachPartName + "' exists on ESB message but is not of type '" + paramType.getName() + "'.");
        }
        return param;
      } else {     
        for(String attachPartNameOnMessage : attachment.getNames()) {
          Object param = attachment.get(attachPartNameOnMessage);
          if(param != null && paramType.isInstance(param)) {
            return param;
          }
        }
      }

      return null;
    }   
  }
}
TOP

Related Classes of org.jboss.soa.esb.listeners.message.BeanContainerAction$AttachmentResolver

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.
'UA-20639858-1', 'auto'); ga('send', 'pageview');