View Javadoc

1   /*
2    * $Id: AxisMessageDispatcher.java 10961 2008-02-22 19:01:02Z dfeist $
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.transport.soap.axis;
12  
13  import org.mule.DefaultMuleMessage;
14  import org.mule.api.MuleEvent;
15  import org.mule.api.MuleMessage;
16  import org.mule.api.config.MuleProperties;
17  import org.mule.api.endpoint.EndpointURI;
18  import org.mule.api.endpoint.ImmutableEndpoint;
19  import org.mule.api.endpoint.OutboundEndpoint;
20  import org.mule.api.transformer.TransformerException;
21  import org.mule.api.transport.DispatchException;
22  import org.mule.config.i18n.CoreMessages;
23  import org.mule.transport.AbstractMessageDispatcher;
24  import org.mule.transport.NullPayload;
25  import org.mule.transport.soap.NamedParameter;
26  import org.mule.transport.soap.SoapConstants;
27  import org.mule.transport.soap.SoapMethod;
28  import org.mule.transport.soap.i18n.SoapMessages;
29  import org.mule.util.BeanUtils;
30  import org.mule.util.StringUtils;
31  import org.mule.util.TemplateParser;
32  
33  import java.util.ArrayList;
34  import java.util.Arrays;
35  import java.util.HashMap;
36  import java.util.Iterator;
37  import java.util.List;
38  import java.util.Map;
39  
40  import javax.activation.DataHandler;
41  import javax.xml.namespace.QName;
42  
43  import org.apache.axis.AxisProperties;
44  import org.apache.axis.EngineConfiguration;
45  import org.apache.axis.MessageContext;
46  import org.apache.axis.attachments.AttachmentPart;
47  import org.apache.axis.client.Call;
48  import org.apache.axis.client.Service;
49  import org.apache.axis.configuration.FileProvider;
50  import org.apache.axis.constants.Style;
51  import org.apache.axis.constants.Use;
52  import org.apache.axis.wsdl.fromJava.Namespaces;
53  import org.apache.axis.wsdl.fromJava.Types;
54  
55  /**
56   * <code>AxisMessageDispatcher</code> is used to make soap requests via the Axis
57   * soap client.
58   */
59  public class AxisMessageDispatcher extends AbstractMessageDispatcher
60  {
61  
62      protected EngineConfiguration clientConfig;
63      protected AxisConnector connector;
64      protected Service service;
65      private Map callParameters;
66  
67      public AxisMessageDispatcher(OutboundEndpoint endpoint)
68      {
69          super(endpoint);
70          this.connector = (AxisConnector)endpoint.getConnector();
71          AxisProperties.setProperty("axis.doAutoTypes", Boolean.toString(connector.isDoAutoTypes()));
72      }
73  
74      protected void doConnect() throws Exception
75      {
76          if (service == null)
77          {
78              service = createService(endpoint);
79          }
80      }
81  
82      protected void doDisconnect() throws Exception
83      {
84          if (service != null)
85          {
86              service = null;
87          }
88      }
89  
90      protected void doDispose()
91      {
92          // template method
93      }
94  
95      protected synchronized EngineConfiguration getClientConfig(ImmutableEndpoint endpoint)
96      {
97          if (clientConfig == null)
98          {
99              // Allow the client config to be set on the endpoint
100             String config;
101             config = (String)endpoint.getProperty(AxisConnector.AXIS_CLIENT_CONFIG_PROPERTY);
102 
103             if (config != null)
104             {
105                 clientConfig = new FileProvider(config);
106             }
107             else
108             {
109                 clientConfig = connector.getClientProvider();
110             }
111         }
112         return clientConfig;
113     }
114 
115     protected Service createService(ImmutableEndpoint endpoint) throws Exception
116     {
117         // Create a simple axis service without wsdl
118         EngineConfiguration config = getClientConfig(endpoint);
119         return new Service(config);
120     }
121 
122     protected void doDispatch(MuleEvent event) throws Exception
123     {
124         Object[] args = getArgs(event);
125         Call call = getCall(event, args);
126         // dont use invokeOneWay here as we are already in a thread pool.
127         // Axis creates a new thread for every invoke one way call. nasty!
128         // Mule overides the default Axis HttpSender to return immediately if
129         // the axis.one.way property is set
130         call.setProperty("axis.one.way", Boolean.TRUE);
131         call.setProperty(MuleProperties.MULE_EVENT_PROPERTY, event);
132         call.invoke(args);
133     }
134 
135     protected MuleMessage doSend(MuleEvent event) throws Exception
136     {
137         Call call;
138         Object result;
139         Object[] args = getArgs(event);
140         call = getCall(event, args);
141         result = call.invoke(args);
142         if (result == null)
143         {
144             return null;
145         }
146         else
147         {
148             MuleMessage resultMessage = new DefaultMuleMessage(result, event.getMessage());
149             setMessageContextProperties(resultMessage, call.getMessageContext());
150             return resultMessage;
151         }
152     }
153 
154     protected Call getCall(MuleEvent event, Object[] args) throws Exception
155     {
156         EndpointURI endpointUri = event.getEndpoint().getEndpointURI();
157         Object method = getInitialMethod(event); // changes object state
158         Call call = (Call) service.createCall();
159         parseStyle(event, call);
160         parseUse(event, call);
161 
162         // set properties on the call from the endpoint properties
163         BeanUtils.populateWithoutFail(call, event.getEndpoint().getProperties(), false);
164         call.setTargetEndpointAddress(endpointUri.getAddress());
165 
166         method = refineMethod(event, call, method);
167         String methodNamespace = call.getOperationName().getNamespaceURI();
168 
169         // set Mule event here so that handlers can extract info
170         call.setProperty(MuleProperties.MULE_EVENT_PROPERTY, event);
171         call.setProperty(MuleProperties.MULE_ENDPOINT_PROPERTY, event.getEndpoint());
172 
173         setCustomProperties(event, call);
174         call.setTimeout(new Integer(event.getTimeout()));
175         setUserCredentials(endpointUri, call);
176 
177         Map methodCalls = (Map)event.getMessage().getProperty(AxisConnector.SOAP_METHODS);
178         if (methodCalls == null && !(method instanceof SoapMethod))
179         {
180             buildSoapMethods(event, call, method, methodNamespace, args);
181         }
182 
183         setCallParams(call, event, call.getOperationName());
184         setSoapAction(event, endpointUri, call);
185         addAttachments(event, call);
186         return call;
187     }
188 
189     protected void addAttachments(MuleEvent event, Call call)
190     {
191         // Add any attachments to the call
192         for (Iterator iterator = event.getMessage().getAttachmentNames().iterator(); iterator.hasNext();)
193         {
194             String name = (String)iterator.next();
195             DataHandler dh = event.getMessage().getAttachment(name);
196             AttachmentPart part = new AttachmentPart(dh);
197             call.addAttachmentPart(part);
198         }
199     }
200     
201     protected void setSoapAction(MuleEvent event, EndpointURI endpointUri, Call call)
202     {
203         // Set custom soap action if set on the event or endpoint
204         String soapAction = (String)event.getMessage().getProperty(SoapConstants.SOAP_ACTION_PROPERTY);
205         if (soapAction != null)
206         {
207             soapAction = parseSoapAction(soapAction, call.getOperationName(), event);
208             call.setSOAPActionURI(soapAction);
209             call.setUseSOAPAction(Boolean.TRUE.booleanValue());
210         }
211         else
212         {
213             call.setSOAPActionURI(endpointUri.getAddress());
214         }
215     }
216 
217     protected void buildSoapMethods(MuleEvent event, Call call, Object method, String methodNamespace, Object[] args)
218     {
219         List params = new ArrayList();
220         for (int i = 0; i < args.length; i++)
221         {
222             if (args[i] == null)
223             {
224                 QName qname = call.getTypeMapping().getTypeQName(Object.class);
225                 params.add("value" + i + ";qname{" + qname.getPrefix() + ":" + qname.getLocalPart() + ":"
226                         + qname.getNamespaceURI() + "};in");
227             }
228             else if (args[i] instanceof DataHandler[])
229             {
230                 params.add("attachments;qname{DataHandler:http://xml.apache.org/xml-soap};in");
231                 // Convert key/value pairs into the parameters
232             }
233             else if (args[i] instanceof Map && connector.isTreatMapAsNamedParams())
234             {
235                 for (Iterator iterator = ((Map)args[i]).entrySet().iterator(); iterator.hasNext();)
236                 {
237                     Map.Entry entry = (Map.Entry)iterator.next();
238                     if (call.getTypeMapping().getTypeQName(entry.getValue().getClass()) != null)
239                     {
240                         QName type = call.getTypeMapping().getTypeQName(entry.getValue().getClass());
241                         params.add("qname{" + entry.getKey().toString()
242                                 + (methodNamespace == null ? "" : ":" + methodNamespace) + "};qname{"
243                                 + type.getPrefix() + ":" + type.getLocalPart() + ":"
244                                 + type.getNamespaceURI() + "};in");
245                     }
246                     else
247                     {
248                         params.add("value" + i + ";qname{"
249                                 + Types.getLocalNameFromFullName(args[i].getClass().getName()) + ":"
250                                 + Namespaces.makeNamespace(args[i].getClass().getName()) + "};in");
251                         params.add("qname{" + entry.getKey().toString()
252                                 + (methodNamespace == null ? "" : ":" + methodNamespace) + "};qname{"
253                                 + Types.getLocalNameFromFullName(args[i].getClass().getName()) + ":"
254                                 + Namespaces.makeNamespace(args[i].getClass().getName()) + "};in");
255                     }
256 
257                 }
258             }
259             else if (call.getTypeMapping().getTypeQName(args[i].getClass()) != null)
260             {
261                 QName qname = call.getTypeMapping().getTypeQName(args[i].getClass());
262                 params.add("value" + i + ";qname{" + qname.getPrefix() + ":" + qname.getLocalPart() + ":"
263                         + qname.getNamespaceURI() + "};in");
264             }
265             else
266             {
267                 params.add("value" + i + ";qname{"
268                         + Types.getLocalNameFromFullName(args[i].getClass().getName()) + ":"
269                         + Namespaces.makeNamespace(args[i].getClass().getName()) + "};in");
270             }
271         }
272 
273         HashMap map = new HashMap();
274         map.put(method, params);
275         event.getMessage().setProperty(AxisConnector.SOAP_METHODS, map);
276     }
277 
278     protected void setUserCredentials(EndpointURI endpointUri, Call call)
279     {
280         if (endpointUri.getUserInfo() != null)
281         {
282             call.setUsername(endpointUri.getUser());
283             call.setPassword(endpointUri.getPassword());
284         }
285     }
286 
287     protected void setCustomProperties(MuleEvent event, Call call)
288     {
289         for (Iterator iter = event.getMessage().getPropertyNames().iterator(); iter.hasNext();)
290         {
291             String key = (String)iter.next();
292             if (!(key.startsWith(MuleProperties.PROPERTY_PREFIX)))
293             {
294                 Object value = event.getMessage().getProperty(key);
295                 if (value != null)
296                 {
297                     call.setProperty(key, value);
298                 }
299             }
300         }
301     }
302 
303     protected Object refineMethod(MuleEvent event, Call call, Object method)
304     {
305         if (method instanceof String)
306         {
307             // Set a custome method namespace if one is set. This will be used forthe
308             // parameters too
309             String methodNamespace = (String)event.getMessage().getProperty(SoapConstants.METHOD_NAMESPACE_PROPERTY);
310             if (methodNamespace != null)
311             {
312                 call.setOperationName(new QName(methodNamespace, method.toString()));
313             }
314             else
315             {
316                 call.setOperationName(new QName(method.toString()));
317             }
318         }
319         else if (method instanceof QName)
320         {
321             call.setOperationName((QName)method);
322             method = ((QName)method).getLocalPart();
323         }
324         else
325         {
326             call.setOperationName(((SoapMethod)method).getName());
327         }
328         return method;
329     }
330 
331     protected void parseUse(MuleEvent event, Call call)
332     {
333         // Set use: Endcoded/Literal
334         String use = event.getMessage().getStringProperty(AxisConnector.USE, null);
335         if (use != null)
336         {
337             Use u = Use.getUse(use);
338             if (u == null)
339             {
340                 throw new IllegalArgumentException(
341                         CoreMessages.valueIsInvalidFor(use, AxisConnector.USE).toString());
342             }
343             else
344             {
345                 call.setOperationUse(u);
346             }
347         }
348     }
349 
350     protected void parseStyle(MuleEvent event, Call call)
351     {
352         // Note that Axis has specific rules to how these two variables are
353         // combined. This is handled for us
354         // Set style: RPC/wrapped/Doc/Message
355         String style = event.getMessage().getStringProperty(AxisConnector.STYLE, null);
356         if (style != null)
357         {
358             Style s = Style.getStyle(style);
359             if (s == null)
360             {
361                 throw new IllegalArgumentException(
362                         CoreMessages.valueIsInvalidFor(style, AxisConnector.STYLE).toString());
363             }
364             else
365             {
366                 call.setOperationStyle(s);
367             }
368         }
369     }
370 
371     protected Object getInitialMethod(MuleEvent event) throws DispatchException
372     {
373         Object method = event.getMessage().getProperty(MuleProperties.MULE_METHOD_PROPERTY);
374         if (method == null)
375         {
376             method = event.getEndpoint().getEndpointURI().getParams().getProperty(MuleProperties.MULE_METHOD_PROPERTY);
377         }
378         if (method == null)
379         {
380             throw new DispatchException(
381                     SoapMessages.cannotInvokeCallWithoutOperation(), event.getMessage(),
382                     event.getEndpoint());
383         }
384         else if (method instanceof SoapMethod)
385         {
386             synchronized (this)
387             {
388                 if (callParameters == null)
389                 {
390                     callParameters = new HashMap();
391                 }
392                 callParameters.put(((SoapMethod)method).getName().getLocalPart(), method);
393             }
394         }
395         return method;
396     }
397 
398     private Object[] getArgs(MuleEvent event) throws TransformerException
399     {
400         Object payload = event.transformMessage();
401         Object[] args;
402         if (payload instanceof Object[])
403         {
404             args = (Object[])payload;
405         }
406         else
407         {
408             args = new Object[]{payload};
409         }
410         if (event.getMessage().getAttachmentNames() != null
411             && event.getMessage().getAttachmentNames().size() > 0)
412         {
413             ArrayList attachments = new ArrayList();
414             Iterator i = event.getMessage().getAttachmentNames().iterator();
415             while (i.hasNext())
416             {
417                 attachments.add(event.getMessage().getAttachment((String)i.next()));
418             }
419             ArrayList temp = new ArrayList(Arrays.asList(args));
420             temp.add(attachments.toArray(new DataHandler[0]));
421             args = temp.toArray();
422         }
423         return args;
424     }
425 
426     protected void setMessageContextProperties(MuleMessage message, MessageContext ctx)
427     {
428         String temp = ctx.getStrProp(MuleProperties.MULE_CORRELATION_ID_PROPERTY);
429         if (StringUtils.isNotBlank(temp))
430         {
431             message.setCorrelationId(temp);
432         }
433         temp = ctx.getStrProp(MuleProperties.MULE_CORRELATION_GROUP_SIZE_PROPERTY);
434         if (StringUtils.isNotBlank(temp))
435         {
436             message.setCorrelationGroupSize(Integer.parseInt(temp));
437         }
438         temp = ctx.getStrProp(MuleProperties.MULE_CORRELATION_SEQUENCE_PROPERTY);
439         if (StringUtils.isNotBlank(temp))
440         {
441             message.setCorrelationSequence(Integer.parseInt(temp));
442         }
443         temp = ctx.getStrProp(MuleProperties.MULE_REPLY_TO_PROPERTY);
444         if (StringUtils.isNotBlank(temp))
445         {
446             message.setReplyTo(temp);
447         }
448     }
449 
450     protected void setMessageContextAttachments(MuleMessage message, MessageContext ctx) throws Exception
451     {
452         int x = 0;
453         for (Iterator iterator = ctx.getMessage().getAttachments(); iterator.hasNext(); x++)
454         {
455             message.addAttachment(String.valueOf(x),
456                 ((AttachmentPart)iterator.next()).getActivationDataHandler());
457         }
458     }
459 
460     protected static MuleMessage createMessage(Object result, Call call)
461     {
462         if (result == null)
463         {
464             result = NullPayload.getInstance();
465         }
466         Map props = new HashMap();
467         Iterator iter = call.getMessageContext().getPropertyNames();
468         Object key;
469         while (iter.hasNext())
470         {
471             key = iter.next();
472             props.put(key, call.getMessageContext().getProperty(key.toString()));
473         }
474         props.put("soap.message", call.getMessageContext().getMessage());
475         call.clearHeaders();
476         call.clearOperation();
477         return new DefaultMuleMessage(result, props);
478     }
479 
480     public String parseSoapAction(String soapAction, QName method, MuleEvent event)
481     {
482         EndpointURI endpointURI = event.getEndpoint().getEndpointURI();
483         Map properties = new HashMap();
484         MuleMessage msg = event.getMessage();
485         for (Iterator iterator = msg.getPropertyNames().iterator(); iterator.hasNext();)
486         {
487             String propertyKey = (String)iterator.next();
488             properties.put(propertyKey, msg.getProperty(propertyKey));
489         }
490         properties.put(MuleProperties.MULE_METHOD_PROPERTY, method.getLocalPart());
491         properties.put("methodNamespace", method.getNamespaceURI());
492         properties.put("address", endpointURI.getAddress());
493         properties.put("scheme", endpointURI.getScheme());
494         properties.put("host", endpointURI.getHost());
495         properties.put("port", String.valueOf(endpointURI.getPort()));
496         properties.put("path", endpointURI.getPath());
497         properties.put("hostInfo", endpointURI.getScheme()
498                                    + "://"
499                                    + endpointURI.getHost()
500                                    + (endpointURI.getPort() > -1
501                                                    ? ":" + String.valueOf(endpointURI.getPort()) : ""));
502         if (event.getService() != null)
503         {
504             properties.put("serviceName", event.getService().getName());
505         }
506 
507         TemplateParser tp = TemplateParser.createSquareBracesStyleParser();
508         soapAction = tp.parse(properties, soapAction);
509 
510         if (logger.isDebugEnabled())
511         {
512             logger.debug("SoapAction for this call is: " + soapAction);
513         }
514         return soapAction;
515     }
516 
517     private void setCallParams(Call call, MuleEvent event, QName method) throws ClassNotFoundException
518     {
519         synchronized (this)
520         {
521             if (callParameters == null)
522             {
523                 loadCallParams(event, method.getNamespaceURI());
524             }
525         }
526 
527         SoapMethod soapMethod = (SoapMethod)event.getMessage()
528             .removeProperty(MuleProperties.MULE_SOAP_METHOD);
529         if (soapMethod == null)
530         {
531             soapMethod = (SoapMethod)callParameters.get(method.getLocalPart());
532         }
533 
534         if (soapMethod != null)
535         {
536             for (Iterator iterator = soapMethod.getNamedParameters().iterator(); iterator.hasNext();)
537             {
538                 NamedParameter parameter = (NamedParameter)iterator.next();
539                 call.addParameter(parameter.getName(), parameter.getType(), parameter.getMode());
540             }
541 
542             if (soapMethod.getReturnType() != null)
543             {
544                 call.setReturnType(soapMethod.getReturnType());
545             }
546             else if (soapMethod.getReturnClass() != null)
547             {
548                 call.setReturnClass(soapMethod.getReturnClass());
549             }
550 
551             call.setOperationName(soapMethod.getName());
552         }
553     }
554 
555     private void loadCallParams(MuleEvent event, String namespace) throws ClassNotFoundException
556     {
557         Map methodCalls = (Map)event.getMessage().getProperty(AxisConnector.SOAP_METHODS);
558         if (methodCalls == null)
559         {
560             return;
561         }
562 
563         Map.Entry entry;
564         SoapMethod soapMethod;
565         callParameters = new HashMap();
566 
567         for (Iterator iterator = methodCalls.entrySet().iterator(); iterator.hasNext();)
568         {
569             entry = (Map.Entry)iterator.next();
570             if (StringUtils.isEmpty(namespace))
571             {
572                 if (entry.getValue() instanceof List)
573                 {
574                     soapMethod = new SoapMethod(entry.getKey().toString(), (List)entry.getValue());
575                 }
576                 else
577                 {
578                     soapMethod = new SoapMethod(entry.getKey().toString(), entry.getValue().toString());
579                 }
580             }
581             else
582             {
583                 if (entry.getValue() instanceof List)
584                 {
585                     soapMethod = new SoapMethod(new QName(namespace, entry.getKey().toString()),
586                         (List)entry.getValue());
587                 }
588                 else
589                 {
590                     soapMethod = new SoapMethod(new QName(namespace, entry.getKey().toString()),
591                         entry.getValue().toString());
592                 }
593             }
594             callParameters.put(soapMethod.getName().getLocalPart(), soapMethod);
595         }
596     }
597 
598 }