Coverage Report - org.mule.impl.model.resolvers.AnnotatedEntryPointResolver
 
Classes in this File Line Coverage Branch Coverage Complexity
AnnotatedEntryPointResolver
0%
0/72
0%
0/34
0
 
 1  
 /*
 2  
  * $Id: AnnotatedEntryPointResolver.java 20477 2010-12-06 23:38:52Z mike.schilling $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 5  
  *
 6  
  * The software in this package is published under the terms of the CPAL v1.0
 7  
  * license, a copy of which has been included with this distribution in the
 8  
  * LICENSE.txt file.
 9  
  */
 10  
 package org.mule.impl.model.resolvers;
 11  
 
 12  
 import org.mule.api.MuleEventContext;
 13  
 import org.mule.api.annotations.meta.Evaluator;
 14  
 import org.mule.api.config.MuleProperties;
 15  
 import org.mule.api.lifecycle.InitialisationException;
 16  
 import org.mule.api.model.InvocationResult;
 17  
 import org.mule.api.transformer.Transformer;
 18  
 import org.mule.api.transformer.TransformerException;
 19  
 import org.mule.api.transport.PropertyScope;
 20  
 import org.mule.config.expression.ExpressionAnnotationsHelper;
 21  
 import org.mule.config.i18n.CoreMessages;
 22  
 import org.mule.expression.transformers.ExpressionTransformer;
 23  
 import org.mule.model.resolvers.AbstractEntryPointResolver;
 24  
 import org.mule.transport.NullPayload;
 25  
 import org.mule.util.ClassUtils;
 26  
 import org.mule.util.annotation.AnnotationUtils;
 27  
 
 28  
 import java.lang.reflect.Method;
 29  
 import java.util.Map;
 30  
 
 31  
 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
 32  
 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
 33  
 
 34  
 import net.sf.cglib.proxy.Enhancer;
 35  
 
 36  
 /**
 37  
  * A Mule {@link org.mule.api.model.EntryPointResolver} implementation that will resolve methods on a service class
 38  
  * that have Mule expression annotations such as {@link org.mule.api.annotations.param.Payload}, {@link org.mule.api.annotations.param.InboundHeaders}
 39  
  * It will transform the received message to match the annotated method arguments. For example -
 40  
  * <code>
 41  
  * public Object doSomething(
 42  
  *         &#64;XPath ("/foo/bar") String bar,
 43  
  *         &#64;Payload Document doc,
 44  
  *         &#64;InboundHeaders("name") String name)
 45  
  *  {
 46  
  *      //do stuff
 47  
  *  }
 48  
  * </code>
 49  
  *
 50  
  * The component method above will be invoked by running the XPath expression on the payload, adding a second parameter as
 51  
  * the payload itself and passing in the header 'name' as the third parameter.
 52  
  *
 53  
  * There are some rules for how components with annotations will be processed -
 54  
  * <ul>
 55  
  * <li>For components with more than one method annotated with Mule expression annotations the method name to call needs
 56  
  * to be set on the incoming message or endpoint using the {@link org.mule.api.config.MuleProperties#MULE_METHOD_PROPERTY} key.</li>
 57  
  * <li>If the component has only one method annotated with Mule expression annotations there is no need to set the method name to invoke</li>
 58  
  * <li>Every parameter on the method needs to be annotated</li>
 59  
  * </ul>
 60  
  *
 61  
  * @see org.mule.api.annotations.param.Payload
 62  
  * @see org.mule.api.annotations.param.InboundHeaders
 63  
  * @see org.mule.api.annotations.param.InboundAttachments
 64  
  * @see org.mule.api.annotations.param.OutboundHeaders
 65  
  * @see org.mule.api.annotations.param.OutboundAttachments
 66  
  * @see org.mule.api.annotations.expressions.Mule
 67  
  *
 68  
  * @since 3.0
 69  
  *
 70  
  */
 71  0
 public class AnnotatedEntryPointResolver extends AbstractEntryPointResolver
 72  
 {
 73  
 
 74  0
     private AtomicBoolean cacheBuilt = new AtomicBoolean(false);
 75  
 
 76  
 
 77  0
     @SuppressWarnings("unchecked")
 78  
     private Map<Method, Transformer> transformerCache = new ConcurrentHashMap();
 79  
 
 80  
     public InvocationResult invoke(Object component, MuleEventContext context) throws Exception
 81  
     {
 82  
         try
 83  
         {
 84  0
             initCache(component, context);
 85  
         }
 86  0
         catch (Exception e)
 87  
         {
 88  0
             InvocationResult result = new InvocationResult(this, InvocationResult.State.NOT_SUPPORTED);
 89  0
             result.setErrorMessage(e.toString());
 90  0
             return result;
 91  0
         }
 92  
 
 93  0
         ConcurrentHashMap methodCache = getMethodCache(component);
 94  0
         if(methodCache.size()==0)
 95  
         {
 96  0
             InvocationResult result = new InvocationResult(this, InvocationResult.State.NOT_SUPPORTED);
 97  0
             result.setErrorMessage("Component: " + component + " doesn't have any annotated methods, skipping.");
 98  0
             return result;
 99  
         }
 100  
 
 101  
         Object[] payload;
 102  0
         Method method = null;
 103  
         //We remove the property here as a workaround to MULE-4769
 104  0
         Object tempMethod = context.getMessage().removeProperty(MuleProperties.MULE_METHOD_PROPERTY, PropertyScope.INVOCATION);
 105  0
         String methodName = null;
 106  0
         if (tempMethod != null && tempMethod instanceof Method)
 107  
         {
 108  0
             method = (Method) tempMethod;
 109  
         }
 110  
         else
 111  
         {
 112  0
             methodName = (String)tempMethod;
 113  
         }
 114  
 
 115  
         //If a method param is set use that over anything else. This is used by the @Reply Callbacks and where annotations are set
 116  
         //on the method
 117  0
         if (methodName != null)
 118  
         {
 119  0
             method = getMethodByName(component, methodName, context);
 120  0
             if (method == null)
 121  
             {
 122  0
                 InvocationResult result = new InvocationResult(this, InvocationResult.State.NOT_SUPPORTED);
 123  0
                 result.setErrorMessage("Method not found: " + methodName + " on object: " + component.getClass() + ". If the component is a proxy there needs to be an interface on the proxy that defines this method");
 124  0
                 return result;
 125  
                 //TODO i18n
 126  
             }
 127  0
             payload = getPayloadForMethod(method, component, context);
 128  
         }
 129  0
         else if (method != null)
 130  
         {
 131  0
             payload = getPayloadForMethod(method, component, context);
 132  
         }
 133  0
         else if (methodCache.size() == 1)
 134  
         {
 135  0
             method = (Method) methodCache.values().iterator().next();
 136  0
             payload = getPayloadForMethod(method, component, context);
 137  
         }
 138  
         else
 139  
         {
 140  0
             InvocationResult result = new InvocationResult(this, InvocationResult.State.FAILED);
 141  0
             result.setErrorMessage("Component: " + component + " has more than one method annotated, which means the target method name needs to be set on the event");
 142  0
             return result;
 143  
         }
 144  0
         return invokeMethod(component, method,
 145  
                 (method.getParameterTypes().length == 0 ? ClassUtils.NO_ARGS : payload));
 146  
     }
 147  
 
 148  
     protected Object[] getPayloadForMethod(Method method, Object component, MuleEventContext context) throws TransformerException, InitialisationException
 149  
     {
 150  0
         Object[] payload = null;
 151  0
         Method m = method;
 152  
         //If we are using cglib enhanced service objects, we need to read annotations from the real component class
 153  0
         if (Enhancer.isEnhanced(component.getClass()))
 154  
         {
 155  
             try
 156  
             {
 157  0
                 m = component.getClass().getSuperclass().getMethod(method.getName(), method.getParameterTypes());
 158  
             }
 159  0
             catch (NoSuchMethodException e)
 160  
             {
 161  0
                 throw new TransformerException(CoreMessages.createStaticMessage(e.getMessage()), e);
 162  0
             }
 163  
         }
 164  
 
 165  0
         if (AnnotationUtils.getParamAnnotationsWithMeta(m, Evaluator.class).size() > 0)
 166  
         {
 167  0
             payload = getPayloadFromMessageWithAnnotations(m, context);
 168  
         }
 169  0
         return payload;
 170  
     }
 171  
 
 172  
     protected Object[] getPayloadFromMessageWithAnnotations(Method method, MuleEventContext context) throws TransformerException, InitialisationException
 173  
     {
 174  0
         ExpressionTransformer trans = (ExpressionTransformer) transformerCache.get(method);
 175  0
         if (trans == null)
 176  
         {
 177  0
             trans = ExpressionAnnotationsHelper.getTransformerForMethodWithAnnotations(method, context.getMuleContext());
 178  0
             transformerCache.put(method, trans);
 179  
         }
 180  
 
 181  0
         Object o = trans.transform(context.getMessage());
 182  0
         if (o instanceof NullPayload)
 183  
         {
 184  0
             return new Object[]{null};
 185  
         }
 186  0
         else if (o.getClass().isArray())
 187  
         {
 188  0
             return (Object[]) o;
 189  
         }
 190  
         else
 191  
         {
 192  0
             return new Object[]{o};
 193  
         }
 194  
     }
 195  
 
 196  
     @Override
 197  
     public String toString()
 198  
     {
 199  0
         final StringBuffer sb = new StringBuffer();
 200  0
         sb.append("AnnotatedEntryPointResolver");
 201  0
         sb.append(", acceptVoidMethods=").append(isAcceptVoidMethods());
 202  0
         sb.append('}');
 203  0
         return sb.toString();
 204  
     }
 205  
 
 206  
 
 207  
     protected void initCache(Object component, MuleEventContext context)
 208  
     {
 209  0
         if (!cacheBuilt.get())
 210  
         {
 211  0
             synchronized (this)
 212  
             {
 213  
                 try
 214  
                 {
 215  0
                     if (!cacheBuilt.get())
 216  
                     {
 217  0
                         for (int i = 0; i < component.getClass().getMethods().length; i++)
 218  
                         {
 219  0
                             Method method = component.getClass().getMethods()[i];
 220  0
                             if (AnnotationUtils.getParamAnnotationsWithMeta(method, Evaluator.class).size() > 0)
 221  
                             {
 222  0
                                 this.addMethodByName(component, method, context);
 223  
                             }
 224  
                         }
 225  
                     }
 226  
                 }
 227  
                 finally
 228  
                 {
 229  0
                     cacheBuilt.set(true);
 230  0
                 }
 231  0
             }
 232  
         }
 233  0
     }
 234  
 }