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