View Javadoc

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