View Javadoc

1   /*
2    * $Id: XFireMessageDispatcher.java 10164 2007-12-28 11:32:14Z marie.rizzo $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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.providers.soap.xfire;
12  
13  import org.mule.config.MuleProperties;
14  import org.mule.impl.MuleMessage;
15  import org.mule.providers.AbstractMessageDispatcher;
16  import org.mule.providers.FatalConnectException;
17  import org.mule.providers.soap.SoapConstants;
18  import org.mule.providers.soap.i18n.SoapMessages;
19  import org.mule.providers.soap.xfire.i18n.XFireMessages;
20  import org.mule.providers.soap.xfire.transport.MuleUniversalTransport;
21  import org.mule.umo.UMOEvent;
22  import org.mule.umo.UMOMessage;
23  import org.mule.umo.endpoint.UMOEndpointURI;
24  import org.mule.umo.endpoint.UMOImmutableEndpoint;
25  import org.mule.umo.provider.DispatchException;
26  import org.mule.umo.transformer.TransformerException;
27  import org.mule.util.ClassUtils;
28  import org.mule.util.StringUtils;
29  import org.mule.util.TemplateParser;
30  
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.HashMap;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Properties;
38  import java.util.Set;
39  
40  import javax.activation.DataHandler;
41  import javax.xml.namespace.QName;
42  
43  import org.codehaus.xfire.XFire;
44  import org.codehaus.xfire.aegis.AegisBindingProvider;
45  import org.codehaus.xfire.aegis.type.TypeMapping;
46  import org.codehaus.xfire.aegis.type.basic.BeanType;
47  import org.codehaus.xfire.client.Client;
48  import org.codehaus.xfire.handler.Handler;
49  import org.codehaus.xfire.service.OperationInfo;
50  import org.codehaus.xfire.service.Service;
51  import org.codehaus.xfire.transport.Transport;
52  
53  /**
54   * The XFireMessageDispatcher is used for making Soap client requests to remote
55   * services.
56   */
57  public class XFireMessageDispatcher extends AbstractMessageDispatcher
58  {
59      // Since the MessageDispatcher is guaranteed to serve a single thread,
60      // the Dispatcher can own the xfire Client as an instance variable
61      protected Client client = null;
62      protected final XFireConnector connector;
63      private final TemplateParser soapActionTemplateParser = TemplateParser.createAntStyleParser();
64  
65      public XFireMessageDispatcher(UMOImmutableEndpoint endpoint)
66      {
67          super(endpoint);
68          this.connector = (XFireConnector) endpoint.getConnector();
69      }
70  
71      protected void doConnect() throws Exception
72      {
73          if (client == null)
74          {
75              final XFire xfire = connector.getXfire();
76              final String serviceName = getServiceName(endpoint);
77              final Service service = xfire.getServiceRegistry().getService(serviceName);
78  
79              if (service == null)
80              {
81                  throw new FatalConnectException(XFireMessages.serviceIsNull(serviceName), this);
82              }
83              
84              List inList = connector.getServerInHandlers();
85              if (inList != null)
86              {
87                  for (int i = 0; i < inList.size(); i++)
88                  {
89                      Handler handler = (Handler) ClassUtils.instanciateClass(
90                                          inList.get(i).toString(), ClassUtils.NO_ARGS, this.getClass());
91                      service.addInHandler(handler);
92                  }
93              }
94              
95              List outList = connector.getServerOutHandlers();
96              if (outList != null)
97              {
98                  for (int i = 0; i < outList.size(); i++)
99                  {
100                     Handler handler = (Handler) ClassUtils.instanciateClass(
101                                         outList.get(i).toString(), ClassUtils.NO_ARGS, this.getClass());
102                     service.addOutHandler(handler);
103                 }
104             }
105 
106             try
107             {
108                 this.client = createXFireClient(endpoint, service, xfire);
109             }
110             catch (Exception ex)
111             {
112                 disconnect();
113                 throw ex;
114             }
115         }
116     }
117 
118     protected Client createXFireClient(UMOImmutableEndpoint endpoint, Service service, XFire xfire) throws Exception
119     {
120         return createXFireClient(endpoint, service, xfire, null);
121     }
122 
123     protected Client createXFireClient(UMOImmutableEndpoint endpoint, Service service, XFire xfire, String transportClass) throws Exception
124     {
125         Class transportClazz;
126 
127         if (connector.getClientTransport() == null)
128         {
129             if (!StringUtils.isBlank(transportClass))
130             {
131                 transportClazz = ClassUtils.loadClass(transportClass, this.getClass());
132             }
133             else
134             {
135                 transportClazz = MuleUniversalTransport.class;
136             }
137         }
138         else
139         {
140             transportClazz = ClassUtils.loadClass(connector.getClientTransport(), this.getClass());
141         }
142         
143         Transport transport = (Transport) transportClazz.getConstructor(null).newInstance(null);
144         Client client = new Client(transport, service, endpoint.getEndpointURI().toString());
145         client.setXFire(xfire);
146         client.setEndpointUri(endpoint.getEndpointURI().toString());
147         return configureXFireClient(client);
148     }
149 
150     protected Client configureXFireClient(Client client) throws Exception
151     {       
152         client.addInHandler(new MuleHeadersInHandler());
153         client.addOutHandler(new MuleHeadersOutHandler());
154 
155         List inList = connector.getClientInHandlers();
156         if (inList != null)
157         {
158             for (int i = 0; i < inList.size(); i++)
159             {
160                 Handler handler = (Handler) ClassUtils.instanciateClass(
161                                         inList.get(i).toString(), ClassUtils.NO_ARGS, this.getClass());
162                 client.addInHandler(handler);
163             }
164         }
165         
166         List outList = connector.getClientOutHandlers();
167         if (outList != null)
168         {
169             for (int i = 0; i < outList.size(); i++)
170             {
171                 Handler handler = (Handler) ClassUtils.instanciateClass(
172                                         outList.get(i).toString(), ClassUtils.NO_ARGS, this.getClass());
173                 client.addOutHandler(handler);
174             }
175         }
176         
177         if (this.connector.getExtraProperties() != null)
178         {
179             Properties props = new Properties();
180             props.putAll(this.connector.getExtraProperties());
181             Object[] keys = props.keySet().toArray();
182             for (int i = 0; i < props.size(); i++)
183             {
184                 client.setProperty(keys[i].toString(), props.getProperty((String) keys[i]));
185             }
186         }
187         
188         return client;
189     }
190 
191     protected void doDisconnect() throws Exception
192     {
193         client = null;
194     }
195 
196     protected void doDispose()
197     {
198         // nothing to do
199     }
200 
201     protected String getMethod(UMOEvent event) throws DispatchException
202     {
203         String method = (String)event.getMessage().getProperty(MuleProperties.MULE_METHOD_PROPERTY);     
204                 
205         if (method == null)
206         {
207             UMOEndpointURI endpointUri = event.getEndpoint().getEndpointURI();
208             method = (String)endpointUri.getParams().get(MuleProperties.MULE_METHOD_PROPERTY);
209         }
210         
211         if (method == null)
212         {
213             method = (String)event.getEndpoint().getProperties().get(MuleProperties.MULE_METHOD_PROPERTY);
214         }   
215         
216         if (method == null)
217         {
218             throw new DispatchException(SoapMessages.cannotInvokeCallWithoutOperation(), 
219                 event.getMessage(), event.getEndpoint());
220         }
221                 
222         return method;
223     }
224 
225     protected Object[] getArgs(UMOEvent event) throws TransformerException
226     {
227         Object payload = event.getTransformedMessage();
228         Object[] args;
229 
230         if (payload instanceof Object[])
231         {
232             args = (Object[])payload;
233         }
234         else
235         {
236             args = new Object[]{payload};
237         }
238 
239         UMOMessage message = event.getMessage();
240         Set attachmentNames = message.getAttachmentNames();
241         if (attachmentNames != null && !attachmentNames.isEmpty())
242         {
243             List attachments = new ArrayList();
244             for (Iterator i = attachmentNames.iterator(); i.hasNext();)
245             {
246                 attachments.add(message.getAttachment((String)i.next()));
247             }
248             List temp = new ArrayList(Arrays.asList(args));
249             temp.add(attachments.toArray(new DataHandler[0]));
250             args = temp.toArray();
251         }
252 
253         return args;
254     }
255 
256     protected UMOMessage doSend(UMOEvent event) throws Exception
257     {
258         if (event.getEndpoint().getProperty("complexTypes") != null)
259         {
260             configureClientForComplexTypes(this.client, event);
261         }
262         this.client.setTimeout(event.getTimeout());
263         this.client.setProperty(MuleProperties.MULE_EVENT_PROPERTY, event);
264         String method = getMethod(event);
265 
266         // Set custom soap action if set on the event or endpoint
267         String soapAction = (String)event.getMessage().getProperty(SoapConstants.SOAP_ACTION_PROPERTY);
268         if (soapAction != null)
269         {
270             soapAction = parseSoapAction(soapAction, new QName(method), event);
271             this.client.setProperty(org.codehaus.xfire.soap.SoapConstants.SOAP_ACTION, soapAction);
272         }
273 
274         // Set Custom Headers on the client
275         Object[] arr = event.getMessage().getPropertyNames().toArray();
276         String head;
277 
278         for (int i = 0; i < arr.length; i++)
279         {
280             head = (String) arr[i];
281             if ((head != null) && (!head.startsWith("MULE")))
282             {
283                 this.client.setProperty((String) arr[i], event.getMessage().getProperty((String) arr[i]));
284             }
285         }
286 
287         Object[] response = client.invoke(method, getArgs(event));
288 
289         UMOMessage result = null;
290         if (response != null && response.length <= 1)
291         {
292             if (response.length == 1)
293             {
294                 result = new MuleMessage(response[0], event.getMessage());
295             }
296         }
297         else
298         {
299             result = new MuleMessage(response, event.getMessage());
300         }
301 
302         return result;
303     }
304 
305     protected void doDispatch(UMOEvent event) throws Exception
306     {
307         this.client.setTimeout(event.getTimeout());
308         this.client.setProperty(MuleProperties.MULE_EVENT_PROPERTY, event);
309         this.client.invoke(getMethod(event), getArgs(event));
310     }
311 
312     /**
313      * Make a specific request to the underlying transport
314      * 
315      * @param timeout the maximum time the operation should block before returning.
316      *            The call should return immediately if there is data available. If
317      *            no data becomes available before the timeout elapses, null will be
318      *            returned
319      * @return the result of the request wrapped in a UMOMessage object. Null will be
320      *         returned if no data was avaialable
321      * @throws Exception if the call to the underlying protocal cuases an exception
322      */
323     protected UMOMessage doReceive(long timeout) throws Exception
324     {
325         String serviceName = getServiceName(endpoint);
326 
327         XFire xfire = connector.getXfire();
328         Service service = xfire.getServiceRegistry().getService(serviceName);
329 
330         Client client = new Client(new MuleUniversalTransport(), service, endpoint.getEndpointURI()
331             .toString());
332         client.setXFire(xfire);
333         client.setTimeout((int)timeout);
334         client.setEndpointUri(endpoint.getEndpointURI().toString());
335 
336         String method = (String)endpoint.getProperty(MuleProperties.MULE_METHOD_PROPERTY);
337         OperationInfo op = service.getServiceInfo().getOperation(method);
338 
339         Properties params = endpoint.getEndpointURI().getUserParams();
340         String args[] = new String[params.size()];
341         int i = 0;
342         for (Iterator iterator = params.values().iterator(); iterator.hasNext(); i++)
343         {
344             args[i] = iterator.next().toString();
345         }
346 
347         Object[] response = client.invoke(op, args);
348 
349         if (response != null && response.length == 1)
350         {
351             return new MuleMessage(response[0]);
352         }
353         else
354         {
355             return new MuleMessage(response);
356         }
357     }
358 
359     /**
360      * Get the service that is mapped to the specified request.
361      * @param endpoint endpoint containing a service path
362      * @return service name
363      */
364     protected String getServiceName(UMOImmutableEndpoint endpoint)
365     {
366         String pathInfo = endpoint.getEndpointURI().getPath();
367 
368         if (StringUtils.isEmpty(pathInfo))
369         {
370             return endpoint.getEndpointURI().getHost();
371         }
372 
373         String serviceName;
374 
375         int i = pathInfo.lastIndexOf('/');
376 
377         if (i > -1)
378         {
379             serviceName = pathInfo.substring(i + 1);
380         }
381         else
382         {
383             serviceName = pathInfo;
384         }
385 
386         return serviceName;
387     }
388 
389     public String parseSoapAction(String soapAction, QName method, UMOEvent event)
390     {
391 
392         UMOEndpointURI endpointURI = event.getEndpoint().getEndpointURI();
393         Map properties = new HashMap();
394         UMOMessage msg = event.getMessage();
395         for (Iterator iterator = msg.getPropertyNames().iterator(); iterator.hasNext();)
396         {
397             String propertyKey = (String)iterator.next();
398             properties.put(propertyKey, msg.getProperty(propertyKey));
399         }
400         properties.put(MuleProperties.MULE_METHOD_PROPERTY, method.getLocalPart());
401         properties.put("methodNamespace", method.getNamespaceURI());
402         properties.put("address", endpointURI.getAddress());
403         properties.put("scheme", endpointURI.getScheme());
404         properties.put("host", endpointURI.getHost());
405         properties.put("port", String.valueOf(endpointURI.getPort()));
406         properties.put("path", endpointURI.getPath());
407         properties.put("hostInfo", endpointURI.getScheme()
408                                    + "://"
409                                    + endpointURI.getHost()
410                                    + (endpointURI.getPort() > -1
411                                                    ? ":" + String.valueOf(endpointURI.getPort()) : ""));
412         if (event.getComponent() != null)
413         {
414             properties.put("serviceName", event.getComponent().getDescriptor().getName());
415         }
416 
417         soapAction = soapActionTemplateParser.parse(properties, soapAction);
418 
419         if (logger.isDebugEnabled())
420         {
421             logger.debug("SoapAction for this call is: " + soapAction);
422         }
423 
424         return soapAction;
425     }
426 
427     protected void configureClientForComplexTypes(Client client, UMOEvent event) throws ClassNotFoundException
428     {
429         Map complexTypes = (Map) event.getEndpoint().getProperty("complexTypes");
430         Object[] beans = complexTypes.keySet().toArray();
431 
432         AegisBindingProvider bp = (AegisBindingProvider) client.getService().getBindingProvider();
433         TypeMapping typeMapping = bp.getTypeMapping(client.getService());
434 
435         // for each complex type
436         for (int i = 0; i < beans.length; i++)
437         {
438             BeanType bt = new BeanType();
439             String[] queue = ((String) complexTypes.get(beans[i])).split(":", 2);
440             bt.setSchemaType(new QName(queue[1], queue[0]));
441             bt.setTypeClass(Class.forName(beans[i].toString()));
442             typeMapping.register(bt);
443         }
444     }
445 }