View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.component;
8   
9   import org.mule.DefaultMuleEvent;
10  import org.mule.DefaultMuleMessage;
11  import org.mule.RequestContext;
12  import org.mule.api.MuleContext;
13  import org.mule.api.MuleEvent;
14  import org.mule.api.MuleMessage;
15  import org.mule.api.component.InterfaceBinding;
16  import org.mule.api.config.MuleProperties;
17  import org.mule.config.i18n.CoreMessages;
18  import org.mule.transport.NullPayload;
19  import org.mule.util.StringMessageUtils;
20  
21  import java.lang.reflect.InvocationHandler;
22  import java.lang.reflect.Method;
23  import java.util.Map;
24  
25  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  
30  public class BindingInvocationHandler implements InvocationHandler
31  {
32  
33      public static final String DEFAULT_METHOD_NAME_TOKEN = "default";
34  
35      protected static Log logger = LogFactory.getLog(BindingInvocationHandler.class);
36  
37      protected Map<String, InterfaceBinding> routers = null;
38  
39      protected MuleContext muleContext;
40  
41      @SuppressWarnings("unchecked")
42      public BindingInvocationHandler(InterfaceBinding router)
43      {
44          routers = new ConcurrentHashMap();
45          addRouterForInterface(router);
46      }
47  
48      public void addRouterForInterface(InterfaceBinding router)
49      {
50          if (router.getMethod() == null)
51          {
52              if (routers.size() == 0)
53              {
54                  routers.put(DEFAULT_METHOD_NAME_TOKEN, router);
55              }
56              else
57              {
58                  throw new IllegalArgumentException(CoreMessages.mustSetMethodNamesOnBinding().getMessage());
59              }
60          }
61          else
62          {
63              routers.put(router.getMethod(), router);
64          }
65          muleContext = router.getEndpoint().getMuleContext();
66      }
67  
68      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
69      {
70          if (method.getName().equals("toString"))
71          {
72              return toString();
73          }
74  
75          MuleMessage message = createMuleMessage(args);
76  
77          // Some transports such as Axis, RMI and EJB can use the method information
78          message.setInvocationProperty(MuleProperties.MULE_METHOD_PROPERTY, method.getName());
79  
80          InterfaceBinding router = routers.get(method.getName());
81          if (router == null)
82          {
83              router = routers.get(DEFAULT_METHOD_NAME_TOKEN);
84          }
85  
86          if (router == null)
87          {
88              throw new IllegalArgumentException(CoreMessages.cannotFindBindingForMethod(method.getName()).toString());
89          }
90  
91          MuleEvent currentEvent = RequestContext.getEvent();
92          MuleEvent replyEvent = router.process(new DefaultMuleEvent(message,currentEvent));
93  
94          if (replyEvent != null && replyEvent.getMessage()!=null)
95          {
96              MuleMessage reply = replyEvent.getMessage();
97              if (reply.getExceptionPayload() != null)
98              {
99                  throw findDeclaredMethodException(method, reply.getExceptionPayload().getException());
100             }
101             else
102             {
103                 return determineReply(reply, method);
104             }
105         }
106         else
107         {
108             return null;
109         }
110     }
111     
112     private MuleMessage createMuleMessage(Object[] args)
113     {
114         if (args == null)
115         {
116             return new DefaultMuleMessage(NullPayload.getInstance(), muleContext);
117         }
118         else if (args.length == 1)
119         {
120             return new DefaultMuleMessage(args[0], muleContext);
121         }
122         else
123         {
124             return new DefaultMuleMessage(args, muleContext);
125         }
126     }
127 
128     /**
129      * Return the causing exception instead of the general "container" exception (typically 
130      * UndeclaredThrowableException) if the cause is known and the type matches one of the
131      * exceptions declared in the given method's "throws" clause.
132      */
133     private Throwable findDeclaredMethodException(Method method, Throwable throwable) throws Throwable 
134     {       
135         Throwable cause = throwable.getCause();
136         if (cause != null)
137         {
138             // Try to find a matching exception type from the method's "throws" clause, and if so
139             // return that exception.
140             Class[] exceptions = method.getExceptionTypes();         
141             for (int i = 0; i < exceptions.length; i++)
142             {
143                 if (cause.getClass().equals(exceptions[i]))
144                 {
145                     return cause; 
146                 }                       
147             }   
148         }
149         
150         return throwable;
151     }
152     
153     private Object determineReply(MuleMessage reply, Method bindingMethod)
154     {
155         if (MuleMessage.class.isAssignableFrom(bindingMethod.getReturnType()))
156         {
157             return reply;
158         }
159         else if (reply.getPayload() == NullPayload.getInstance() && !bindingMethod.getReturnType().isInstance(reply.getPayload()))
160         {
161             return null;
162         }
163         else
164         {
165             return reply.getPayload();
166         }
167     }
168     
169     @Override
170     public String toString()
171     {
172         final StringBuffer sb = new StringBuffer();
173         sb.append("BindingInvocation");
174         sb.append("{routers='").append(StringMessageUtils.toString(routers));
175         sb.append('}');
176         return sb.toString();
177     }
178     
179 }