View Javadoc

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