View Javadoc

1   /*
2    * $Id: IBeanBinding.java 19974 2010-10-21 10:57:04Z rossmason $
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.module.ibeans.config;
11  
12  import org.mule.DefaultMuleEvent;
13  import org.mule.api.EndpointAnnotationParser;
14  import org.mule.api.MessagingException;
15  import org.mule.api.MuleContext;
16  import org.mule.api.MuleEvent;
17  import org.mule.api.MuleException;
18  import org.mule.api.MuleRuntimeException;
19  import org.mule.api.component.InterfaceBinding;
20  import org.mule.api.endpoint.ImmutableEndpoint;
21  import org.mule.api.endpoint.InboundEndpoint;
22  import org.mule.api.endpoint.OutboundEndpoint;
23  import org.mule.config.i18n.CoreMessages;
24  import org.mule.module.ibeans.spi.MuleCallAnnotationHandler;
25  import org.mule.module.ibeans.spi.MuleIBeansPlugin;
26  import org.mule.module.ibeans.spi.support.DynamicRequestInterfaceBinding;
27  import org.mule.util.annotation.AnnotationMetaData;
28  import org.mule.util.annotation.AnnotationUtils;
29  
30  import java.lang.annotation.Annotation;
31  import java.lang.reflect.InvocationHandler;
32  import java.lang.reflect.Method;
33  import java.lang.reflect.Proxy;
34  import java.util.Collection;
35  import java.util.Collections;
36  import java.util.HashMap;
37  import java.util.Iterator;
38  import java.util.List;
39  import java.util.Map;
40  
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  import org.ibeans.annotation.Call;
44  import org.ibeans.annotation.Template;
45  import org.ibeans.annotation.param.Body;
46  import org.ibeans.annotation.param.BodyParam;
47  import org.ibeans.annotation.param.HeaderParam;
48  import org.ibeans.api.IBeanInvoker;
49  import org.ibeans.api.IBeansException;
50  import org.ibeans.api.channel.HTTP;
51  import org.ibeans.impl.IntegrationBeanInvocationHandler;
52  import org.ibeans.impl.InvokeAnnotationHandler;
53  import org.ibeans.impl.TemplateAnnotationHandler;
54  
55  /**
56   * TODO
57   */
58  public class IBeanBinding implements InterfaceBinding
59  {
60  
61      private static final Log logger = LogFactory.getLog(IBeanBinding.class);
62  
63      private Class interfaceClass;
64  
65      // The endpoint used to actually dispatch the message
66      protected OutboundEndpoint endpoint;
67  
68      protected IBeanFlowConstruct flow;
69  
70      protected MuleIBeansPlugin plugin;
71      
72      protected MuleContext muleContext;
73  
74      public IBeanBinding(IBeanFlowConstruct flow, MuleIBeansPlugin plugin)
75      {
76          this.flow = flow;
77          this.muleContext = this.flow.getMuleContext();
78          this.plugin = plugin;
79      }
80  
81      public String getMethod()
82      {
83          throw new UnsupportedOperationException();
84      }
85  
86      public void setMethod(String method)
87      {
88          throw new UnsupportedOperationException();
89      }
90  
91      public MuleEvent process(MuleEvent event) throws MessagingException
92      {
93          try
94          {
95              return endpoint.process(new DefaultMuleEvent(event.getMessage(), endpoint, event.getSession()));
96          }
97          catch (MessagingException e)
98          {
99              throw e;
100         }
101         catch (MuleException e)
102         {
103             throw new MessagingException(e.getI18nMessage(), event, e);
104         }
105     }
106 
107     public void setInterface(Class interfaceClass)
108     {
109         this.interfaceClass = interfaceClass;
110     }
111 
112     public Class getInterface()
113     {
114         return interfaceClass;
115     }
116 
117     public Object createProxy(Object target)
118     {
119         Map<String, String> evals = new HashMap<String, String>();
120         try
121         {
122             IBeanInvoker<MuleCallAnnotationHandler, TemplateAnnotationHandler, InvokeAnnotationHandler> invoker = plugin.getIBeanInvoker();
123             invoker.getCallHandler().setFlow(flow);
124 
125             List<AnnotationMetaData> annos = AnnotationUtils.getAllMethodAnnotations(getInterface());
126             for (AnnotationMetaData metaData : annos)
127             {
128                 if (metaData.getAnnotation() instanceof Call)
129                 {
130                     Collection c = muleContext.getRegistry().lookupObjects(EndpointAnnotationParser.class);
131                     String scheme;
132                     boolean http;
133                     String uri = ((Call) metaData.getAnnotation()).uri();
134                     int i = uri.indexOf(":/");
135                     if (i == -1)
136                     {
137                         scheme = "dynamic";
138                     }
139                     else
140                     {
141                         scheme = uri.substring(0, i);
142                     }
143                     http = scheme.contains("http");
144 
145                     Map metaInfo = new HashMap();
146                     //By setting the connectorName we ensure that only one connector is created for each iBean
147                     metaInfo.put("connectorName", metaData.getClazz().getSimpleName() + "." + scheme); //RM*  THis affects the connector name generation + "#" + target.hashCode());
148 
149                     for (Iterator iterator = c.iterator(); iterator.hasNext();)
150                     {
151                         EndpointAnnotationParser parser = (EndpointAnnotationParser) iterator.next();
152                         if (parser.supports(metaData.getAnnotation(), metaData.getClazz(), metaData.getMember()))
153                         {
154                             InterfaceBinding binding;
155                             Method method = (Method) metaData.getMember();
156                             boolean callChannel = false;
157                             Annotation ann;
158                             //This is a little messy, but we need to detect whether we are doing a Mule 'send' or Mule 'request' call.
159                             //Request calls get data from a resource such as DB, email inbox or message queue. These types of request will
160                             //not have any payload or headers defined.
161                             //The other way to handle this is to introduce a new annotation to explicitly handle this (See the Get annotation).
162                             //The issue is it may be difficult for the user to understand the difference between @Call and @Get. Instead we figure it out
163                             //here.
164                             for (int x = 0; x < method.getParameterAnnotations().length; x++)
165                             {
166                                 ann = method.getParameterAnnotations()[x][0];
167                                 if (ann.annotationType().equals(Body.class) ||
168                                         ann.annotationType().equals(BodyParam.class) ||
169                                         ann.annotationType().equals(HeaderParam.class))
170                                 {
171 
172                                     callChannel = true;
173 
174                                     break;
175                                 }
176                             }
177                             //TODO remove the HTTP hack above. Its required becuase HTTP request on the dispatcher
178                             //don't honour authenitcation for some reason.  Also even though there may not be any headers
179                             //defined we still need to attach some headers to the HTTP method. This is very difficult when
180                             //using request
181                             if (callChannel || http)
182                             {
183                                 OutboundEndpoint endpoint = parser.parseOutboundEndpoint(metaData.getAnnotation(), metaInfo);
184                                 binding = new CallInterfaceBinding(this.flow);
185                                 binding.setEndpoint(endpoint);
186                             }
187                             else
188                             {
189                                 InboundEndpoint endpoint = parser.parseInboundEndpoint(metaData.getAnnotation(), Collections.EMPTY_MAP);
190                                 binding = new DynamicRequestInterfaceBinding();
191                                 binding.setEndpoint(endpoint);
192                             }
193                             //We need to differenciate between GET and POST
194                             //TODO Consider making this explicit since an iBeans is really a service interaction definition
195                             if (http)
196                             {
197                                 List<AnnotationMetaData> temp = AnnotationUtils.getParamAnnotations(method);
198                                 boolean post = false;
199                                 for (AnnotationMetaData data : temp)
200                                 {
201                                     if (data.getAnnotation().annotationType().equals(Body.class) ||
202                                             data.getAnnotation().annotationType().equals(BodyParam.class))
203                                     {
204                                         post = true;
205                                         break;
206                                     }
207                                 }
208                                 //By default Mule will post if no method is set
209                                 if (!post && binding.getEndpoint().getProperties().get(HTTP.METHOD_KEY) == null)
210                                 {
211                                     binding.getEndpoint().getProperties().put(HTTP.METHOD_KEY, "GET");
212                                 }
213                             }
214 
215                             binding.setInterface(getInterface());
216                             binding.setMethod(metaData.getMember().toString());
217                             invoker.getCallHandler().addRouterForInterface(binding);
218 
219                         }
220                     }
221                 }
222                 else if (metaData.getAnnotation() instanceof Template)
223                 {
224                     evals.put(metaData.getMember().toString(), ((Template) metaData.getAnnotation()).value());
225                 }
226             }
227 
228             if (evals.size() > 0)
229             {
230                 invoker.getTemplateHandler().setEvals(evals);
231             }
232 
233             Object proxy = Proxy.newProxyInstance(getInterface().getClassLoader(), new Class[]{getInterface()}, createInvocationHandler());
234             if (logger.isDebugEnabled())
235             {
236                 logger.debug("Have proxy?: " + (null != proxy));
237             }
238             return proxy;
239 
240         }
241         catch (Exception e)
242         {
243             throw new MuleRuntimeException(CoreMessages.failedToCreateProxyFor(target), e);
244         }
245     }
246 
247     public void setEndpoint(ImmutableEndpoint e)
248     {
249         endpoint = (OutboundEndpoint) e;
250     }
251 
252     public String toString()
253     {
254         final StringBuffer sb = new StringBuffer();
255         sb.append("IBeanBinding");
256         sb.append(", interface=").append(interfaceClass);
257         sb.append('}');
258         return sb.toString();
259     }
260 
261     public ImmutableEndpoint getEndpoint()
262     {
263         return endpoint;
264     }
265 
266     protected InvocationHandler createInvocationHandler() throws IBeansException
267     {
268         return new IntegrationBeanInvocationHandler(interfaceClass, plugin);
269     }
270 }