View Javadoc

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