View Javadoc

1   /*
2    * $Id: XFireMessageDispatcher.java 7976 2007-08-21 14:26:13Z dirk.olmes $
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         return client;
177     }
178 
179     protected void doDisconnect() throws Exception
180     {
181         client = null;
182     }
183 
184     protected void doDispose()
185     {
186         // nothing to do
187     }
188 
189     protected String getMethod(UMOEvent event) throws DispatchException
190     {
191         String method = (String)event.getMessage().getProperty(MuleProperties.MULE_METHOD_PROPERTY);     
192                 
193         if (method == null)
194         {
195             UMOEndpointURI endpointUri = event.getEndpoint().getEndpointURI();
196             method = (String)endpointUri.getParams().get(MuleProperties.MULE_METHOD_PROPERTY);
197         }
198         
199         if (method == null)
200         {
201             method = (String)event.getEndpoint().getProperties().get(MuleProperties.MULE_METHOD_PROPERTY);
202         }   
203         
204         if (method == null)
205         {
206             throw new DispatchException(SoapMessages.cannotInvokeCallWithoutOperation(), 
207                 event.getMessage(), event.getEndpoint());
208         }
209                 
210         return method;
211     }
212 
213     protected Object[] getArgs(UMOEvent event) throws TransformerException
214     {
215         Object payload = event.getTransformedMessage();
216         Object[] args;
217 
218         if (payload instanceof Object[])
219         {
220             args = (Object[])payload;
221         }
222         else
223         {
224             args = new Object[]{payload};
225         }
226 
227         UMOMessage message = event.getMessage();
228         Set attachmentNames = message.getAttachmentNames();
229         if (attachmentNames != null && !attachmentNames.isEmpty())
230         {
231             List attachments = new ArrayList();
232             for (Iterator i = attachmentNames.iterator(); i.hasNext();)
233             {
234                 attachments.add(message.getAttachment((String)i.next()));
235             }
236             List temp = new ArrayList(Arrays.asList(args));
237             temp.add(attachments.toArray(new DataHandler[0]));
238             args = temp.toArray();
239         }
240 
241         return args;
242     }
243 
244     protected UMOMessage doSend(UMOEvent event) throws Exception
245     {
246         if (event.getEndpoint().getProperty("complexTypes") != null)
247         {
248             configureClientForComplexTypes(this.client, event);
249         }
250         this.client.setTimeout(event.getTimeout());
251         this.client.setProperty(MuleProperties.MULE_EVENT_PROPERTY, event);
252         String method = getMethod(event);
253 
254         // Set custom soap action if set on the event or endpoint
255         String soapAction = (String)event.getMessage().getProperty(SoapConstants.SOAP_ACTION_PROPERTY);
256         if (soapAction != null)
257         {
258             soapAction = parseSoapAction(soapAction, new QName(method), event);
259             this.client.setProperty(org.codehaus.xfire.soap.SoapConstants.SOAP_ACTION, soapAction);
260         }
261 
262         // Set Custom Headers on the client
263         Object[] arr = event.getMessage().getPropertyNames().toArray();
264         String head;
265 
266         for (int i = 0; i < arr.length; i++)
267         {
268             head = (String) arr[i];
269             if ((head != null) && (!head.startsWith("MULE")))
270             {
271                 this.client.setProperty((String) arr[i], event.getMessage().getProperty((String) arr[i]));
272             }
273         }
274 
275         Object[] response = client.invoke(method, getArgs(event));
276 
277         UMOMessage result = null;
278         if (response != null && response.length <= 1)
279         {
280             if (response.length == 1)
281             {
282                 result = new MuleMessage(response[0], event.getMessage());
283             }
284         }
285         else
286         {
287             result = new MuleMessage(response, event.getMessage());
288         }
289 
290         return result;
291     }
292 
293     protected void doDispatch(UMOEvent event) throws Exception
294     {
295         this.client.setTimeout(event.getTimeout());
296         this.client.setProperty(MuleProperties.MULE_EVENT_PROPERTY, event);
297         this.client.invoke(getMethod(event), getArgs(event));
298     }
299 
300     /**
301      * Make a specific request to the underlying transport
302      * 
303      * @param timeout the maximum time the operation should block before returning.
304      *            The call should return immediately if there is data available. If
305      *            no data becomes available before the timeout elapses, null will be
306      *            returned
307      * @return the result of the request wrapped in a UMOMessage object. Null will be
308      *         returned if no data was avaialable
309      * @throws Exception if the call to the underlying protocal cuases an exception
310      */
311     protected UMOMessage doReceive(long timeout) throws Exception
312     {
313         String serviceName = getServiceName(endpoint);
314 
315         XFire xfire = connector.getXfire();
316         Service service = xfire.getServiceRegistry().getService(serviceName);
317 
318         Client client = new Client(new MuleUniversalTransport(), service, endpoint.getEndpointURI()
319             .toString());
320         client.setXFire(xfire);
321         client.setTimeout((int)timeout);
322         client.setEndpointUri(endpoint.getEndpointURI().toString());
323 
324         String method = (String)endpoint.getProperty(MuleProperties.MULE_METHOD_PROPERTY);
325         OperationInfo op = service.getServiceInfo().getOperation(method);
326 
327         Properties params = endpoint.getEndpointURI().getUserParams();
328         String args[] = new String[params.size()];
329         int i = 0;
330         for (Iterator iterator = params.values().iterator(); iterator.hasNext(); i++)
331         {
332             args[i] = iterator.next().toString();
333         }
334 
335         Object[] response = client.invoke(op, args);
336 
337         if (response != null && response.length == 1)
338         {
339             return new MuleMessage(response[0]);
340         }
341         else
342         {
343             return new MuleMessage(response);
344         }
345     }
346 
347     /**
348      * Get the service that is mapped to the specified request.
349      * @param endpoint endpoint containing a service path
350      * @return service name
351      */
352     protected String getServiceName(UMOImmutableEndpoint endpoint)
353     {
354         String pathInfo = endpoint.getEndpointURI().getPath();
355 
356         if (StringUtils.isEmpty(pathInfo))
357         {
358             return endpoint.getEndpointURI().getHost();
359         }
360 
361         String serviceName;
362 
363         int i = pathInfo.lastIndexOf('/');
364 
365         if (i > -1)
366         {
367             serviceName = pathInfo.substring(i + 1);
368         }
369         else
370         {
371             serviceName = pathInfo;
372         }
373 
374         return serviceName;
375     }
376 
377     public String parseSoapAction(String soapAction, QName method, UMOEvent event)
378     {
379 
380         UMOEndpointURI endpointURI = event.getEndpoint().getEndpointURI();
381         Map properties = new HashMap();
382         UMOMessage msg = event.getMessage();
383         for (Iterator iterator = msg.getPropertyNames().iterator(); iterator.hasNext();)
384         {
385             String propertyKey = (String)iterator.next();
386             properties.put(propertyKey, msg.getProperty(propertyKey));
387         }
388         properties.put(MuleProperties.MULE_METHOD_PROPERTY, method.getLocalPart());
389         properties.put("methodNamespace", method.getNamespaceURI());
390         properties.put("address", endpointURI.getAddress());
391         properties.put("scheme", endpointURI.getScheme());
392         properties.put("host", endpointURI.getHost());
393         properties.put("port", String.valueOf(endpointURI.getPort()));
394         properties.put("path", endpointURI.getPath());
395         properties.put("hostInfo", endpointURI.getScheme()
396                                    + "://"
397                                    + endpointURI.getHost()
398                                    + (endpointURI.getPort() > -1
399                                                    ? ":" + String.valueOf(endpointURI.getPort()) : ""));
400         if (event.getComponent() != null)
401         {
402             properties.put("serviceName", event.getComponent().getDescriptor().getName());
403         }
404 
405         soapAction = soapActionTemplateParser.parse(properties, soapAction);
406 
407         if (logger.isDebugEnabled())
408         {
409             logger.debug("SoapAction for this call is: " + soapAction);
410         }
411 
412         return soapAction;
413     }
414 
415     protected void configureClientForComplexTypes(Client client, UMOEvent event) throws ClassNotFoundException
416     {
417         Map complexTypes = (Map) event.getEndpoint().getProperty("complexTypes");
418         Object[] beans = complexTypes.keySet().toArray();
419 
420         AegisBindingProvider bp = (AegisBindingProvider) client.getService().getBindingProvider();
421         TypeMapping typeMapping = bp.getTypeMapping(client.getService());
422 
423         // for each complex type
424         for (int i = 0; i < beans.length; i++)
425         {
426             BeanType bt = new BeanType();
427             String[] queue = ((String) complexTypes.get(beans[i])).split(":", 2);
428             bt.setSchemaType(new QName(queue[1], queue[0]));
429             bt.setTypeClass(Class.forName(beans[i].toString()));
430             typeMapping.register(bt);
431         }
432     }
433 }