View Javadoc

1   /*
2    * $Id: AxisMessageReceiver.java 19191 2010-08-25 21:05:23Z tcarlson $
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.api.MuleException;
14  import org.mule.api.component.Component;
15  import org.mule.api.component.JavaComponent;
16  import org.mule.api.construct.FlowConstruct;
17  import org.mule.api.endpoint.EndpointURI;
18  import org.mule.api.endpoint.InboundEndpoint;
19  import org.mule.api.lifecycle.CreateException;
20  import org.mule.api.lifecycle.InitialisationException;
21  import org.mule.api.service.Service;
22  import org.mule.api.transport.Connector;
23  import org.mule.component.AbstractJavaComponent;
24  import org.mule.config.i18n.CoreMessages;
25  import org.mule.transport.AbstractMessageReceiver;
26  import org.mule.transport.soap.axis.extensions.MuleMsgProvider;
27  import org.mule.transport.soap.axis.extensions.MuleRPCProvider;
28  import org.mule.transport.soap.axis.i18n.AxisMessages;
29  import org.mule.util.StringUtils;
30  
31  import java.util.HashMap;
32  import java.util.Iterator;
33  import java.util.List;
34  import java.util.Map;
35  
36  import javax.xml.rpc.ParameterMode;
37  
38  import org.apache.axis.AxisProperties;
39  import org.apache.axis.constants.Style;
40  import org.apache.axis.constants.Use;
41  import org.apache.axis.description.OperationDesc;
42  import org.apache.axis.description.ParameterDesc;
43  import org.apache.axis.handlers.soap.SOAPService;
44  import org.apache.axis.providers.java.JavaProvider;
45  import org.apache.axis.wsdl.fromJava.Namespaces;
46  
47  /**
48   * <code>AxisMessageReceiver</code> is used to register a component as a service
49   * with a Axis server.
50   */
51  
52  public class AxisMessageReceiver extends AbstractMessageReceiver
53  {
54  
55      public static final String AXIS_OPTIONS = "axisOptions";
56      public static final String BEAN_TYPES = "beanTypes";
57      public static final String SERVICE_NAMESPACE = "serviceNamespace";
58  
59      protected AxisConnector connector;
60      protected SOAPService soapService;
61  
62      public AxisMessageReceiver(Connector connector, FlowConstruct flowConstruct, InboundEndpoint endpoint)
63              throws CreateException
64      {
65  
66          super(connector, flowConstruct, endpoint);
67  
68          this.connector = (AxisConnector) connector;
69          try
70          {
71              AxisServiceProxy.setProperties(endpoint.getProperties());
72              create();
73          }
74          catch (Exception e)
75          {
76              throw new CreateException(e, this);
77          }
78      }
79  
80      protected void create() throws Exception
81      {
82          if (!(flowConstruct instanceof Service))
83          {
84              throw new IllegalArgumentException(
85                  "Only the Service flow constuct is supported by the axis transport");
86          }
87          Service service = (Service) flowConstruct;
88          
89          
90          AxisProperties.setProperty("axis.doAutoTypes", String.valueOf(connector.isDoAutoTypes()));
91          String style = (String) endpoint.getProperties().get(AxisConnector.STYLE);
92          String use = (String) endpoint.getProperties().get(AxisConnector.USE);
93          String doc = (String) endpoint.getProperties().get("documentation");
94  
95          EndpointURI uri = endpoint.getEndpointURI();
96          String serviceName = flowConstruct.getName();
97  
98          SOAPService existing = this.connector.getAxis().getService(serviceName);
99          if (existing != null)
100         {
101             soapService = existing;
102             logger.debug("Using existing service for " + serviceName);
103         }
104         else
105         {
106             // Check if the style is message. If so, we need to create
107             // a message oriented provider
108             if (style != null && style.equalsIgnoreCase("message"))
109             {
110                 logger.debug("Creating Message Provider");
111                 soapService = new SOAPService(new MuleMsgProvider(connector));
112                 // } else if (style != null && style.equalsIgnoreCase("document")) {
113                 // logger.debug("Creating Doc Provider");
114                 // service = new SOAPService(new MuleDocLitProvider(connector));
115             }
116             else
117             {
118                 logger.debug("Creating RPC Provider");
119                 soapService = new SOAPService(new MuleRPCProvider(connector));
120             }
121 
122             soapService.setEngine(connector.getAxis());
123         }
124 
125         String servicePath = uri.getPath();
126         soapService.setOption(serviceName, this);
127         soapService.setOption(AxisConnector.SERVICE_PROPERTY_SERVCE_PATH, servicePath);
128         soapService.setOption(AxisConnector.SERVICE_PROPERTY_COMPONENT_NAME, serviceName);
129 
130         soapService.setName(serviceName);
131 
132         // Add any custom options from the Descriptor config
133         Map options = (Map) endpoint.getProperties().get(AXIS_OPTIONS);
134 
135         // IF wsdl service name is not set, default to service name
136         if (options == null)
137         {
138             options = new HashMap(2);
139         }
140         if (options.get("wsdlServiceElement") == null)
141         {
142             options.put("wsdlServiceElement", serviceName);
143         }
144 
145         Map.Entry entry;
146         for (Iterator iterator = options.entrySet().iterator(); iterator.hasNext();)
147         {
148             entry = (Map.Entry) iterator.next();
149             soapService.setOption(entry.getKey().toString(), entry.getValue());
150             logger.debug("Adding Axis option: " + entry);
151         }
152 
153         // set method names
154         Class[] interfaces = AxisServiceProxy.getInterfacesForComponent(service);
155         if (interfaces.length == 0)
156         {
157             throw new InitialisationException(
158                     AxisMessages.objectMustImplementAnInterface(serviceName), service);
159         }
160         // You must supply a class name if you want to restrict methods
161         // or specify the 'allowedMethods' property in the axisOptions property
162         String methodNames = "*";
163 
164         Map methods = (Map) endpoint.getProperties().get(AxisConnector.SOAP_METHODS);
165         if (methods != null)
166         {
167             Iterator i = methods.keySet().iterator();
168             StringBuffer buf = new StringBuffer(64);
169             while (i.hasNext())
170             {
171                 String name = (String) i.next();
172                 Object m = methods.get(name);
173                 SoapMethod method;
174                 if (m instanceof List)
175                 {
176                     method = new SoapMethod(name, (List) m);
177                 }
178                 else
179                 {
180                     method = new SoapMethod(name, (String) m);
181                 }
182 
183                 List namedParameters = method.getNamedParameters();
184                 ParameterDesc[] parameters = new ParameterDesc[namedParameters.size()];
185                 for (int j = 0; j < namedParameters.size(); j++)
186                 {
187                     NamedParameter parameter = (NamedParameter) namedParameters.get(j);
188                     byte mode = ParameterDesc.INOUT;
189                     if (parameter.getMode().equals(ParameterMode.IN))
190                     {
191                         mode = ParameterDesc.IN;
192                     }
193                     else if (parameter.getMode().equals(ParameterMode.OUT))
194                     {
195                         mode = ParameterDesc.OUT;
196                     }
197 
198                     parameters[j] = new ParameterDesc(parameter.getName(), mode, parameter.getType());
199                 }
200 
201                 soapService.getServiceDescription().addOperationDesc(
202                         new OperationDesc(method.getName().getLocalPart(), parameters, method.getReturnType()));
203                 buf.append(method.getName().getLocalPart() + ",");
204             }
205             methodNames = buf.toString();
206             methodNames = methodNames.substring(0, methodNames.length() - 1);
207         }
208         else
209         {
210             String[] methodNamesArray = AxisServiceProxy.getMethodNames(interfaces);
211             StringBuffer buf = new StringBuffer(64);
212             for (int i = 0; i < methodNamesArray.length; i++)
213             {
214                 buf.append(methodNamesArray[i]).append(",");
215             }
216             methodNames = buf.toString();
217             methodNames = methodNames.substring(0, methodNames.length() - 1);
218         }
219 
220         String className = interfaces[0].getName();
221         // The namespace of the service.
222         // Todo use the service qname in Mule 2.0
223         String namespace = (String) endpoint.getProperties().get(SERVICE_NAMESPACE);
224         if (namespace == null)
225         {
226             namespace = Namespaces.makeNamespace(className);
227         }
228 
229         // WSDL override
230         String wsdlFile = (String) endpoint.getProperties().get("wsdlFile");
231         if (wsdlFile != null)
232         {
233             soapService.getServiceDescription().setWSDLFile(wsdlFile);
234         }
235         /*
236          * Now we set up the various options for the SOAPService. We set:
237          * RPCProvider.OPTION_WSDL_SERVICEPORT In essense, this is our service name
238          * RPCProvider.OPTION_CLASSNAME This tells the serverProvider (whether it be
239          * an AvalonProvider or just JavaProvider) what class to load via
240          * "makeNewServiceObject". RPCProvider.OPTION_SCOPE How long the object
241          * loaded via "makeNewServiceObject" will persist - either request, session,
242          * or application. We use the default for now.
243          * RPCProvider.OPTION_WSDL_TARGETNAMESPACE A namespace created from the
244          * package name of the service. RPCProvider.OPTION_ALLOWEDMETHODS What
245          * methods the service can execute on our class. We don't set:
246          * RPCProvider.OPTION_WSDL_PORTTYPE RPCProvider.OPTION_WSDL_SERVICEELEMENT
247          */
248         setOptionIfNotset(soapService, JavaProvider.OPTION_WSDL_SERVICEPORT, serviceName);
249         setOptionIfNotset(soapService, JavaProvider.OPTION_CLASSNAME, className);
250         setOptionIfNotset(soapService, JavaProvider.OPTION_SCOPE, "Request");
251         if (StringUtils.isNotBlank(namespace))
252         {
253             setOptionIfNotset(soapService, JavaProvider.OPTION_WSDL_TARGETNAMESPACE, namespace);
254         }
255 
256         // Set the allowed methods, allow all if there are none specified.
257         if (methodNames == null)
258         {
259             setOptionIfNotset(soapService, JavaProvider.OPTION_ALLOWEDMETHODS, "*");
260         }
261         else
262         {
263             setOptionIfNotset(soapService, JavaProvider.OPTION_ALLOWEDMETHODS, methodNames);
264         }
265 
266         // Note that Axis has specific rules to how these two variables are
267         // combined. This is handled for us
268         // Set style: RPC/wrapped/Doc/Message
269 
270         if (style != null)
271         {
272             Style s = Style.getStyle(style);
273             if (s == null)
274             {
275                 throw new CreateException(
276                         CoreMessages.valueIsInvalidFor(style, AxisConnector.STYLE), this);
277             }
278             else
279             {
280                 soapService.setStyle(s);
281             }
282         }
283         // Set use: Endcoded/Literal
284         if (use != null)
285         {
286             Use u = Use.getUse(use);
287             if (u == null)
288             {
289                 throw new CreateException(CoreMessages.valueIsInvalidFor(use, AxisConnector.USE),
290                         this);
291             }
292             else
293             {
294                 soapService.setUse(u);
295             }
296         }
297 
298         soapService.getServiceDescription().setDocumentation(doc);
299 
300         // Tell Axis to try and be intelligent about serialization.
301         // TypeMappingRegistryImpl registry = (TypeMappingRegistryImpl)
302         // service.getTypeMappingRegistry();
303         // TypeMappingImpl tm = (TypeMappingImpl) registry.();
304 
305         // Handle complex bean type automatically
306         // registry.setDoAutoTypes( true );
307 
308         // Axis 1.2 fix to handle autotypes properly
309         // AxisProperties.setProperty("axis.doAutoTypes",
310         // String.valueOf(connector.isDoAutoTypes()));
311 
312         // TODO Load any explicitly defined bean types
313         // List types = (List) descriptor.getProperties().get(BEAN_TYPES);
314         // connector.registerTypes(registry, types);
315 
316         soapService.setName(serviceName);
317 
318         // Add initialisation callback for the Axis service
319         Component component = service.getComponent();
320         if (component instanceof JavaComponent)
321         {
322             ((AbstractJavaComponent) component).getObjectFactory().addObjectInitialisationCallback(
323                 new AxisInitialisationCallback(soapService));
324         }
325 
326         if (uri.getScheme().equalsIgnoreCase("servlet"))
327         {
328             connector.addServletService(soapService);
329             String endpointUrl = uri.getAddress() + "/" + serviceName;
330             endpointUrl = endpointUrl.replaceFirst("servlet:", "http:");
331             soapService.getServiceDescription().setEndpointURL(endpointUrl);
332         }
333         else
334         {
335             soapService.getServiceDescription().setEndpointURL(uri.getAddress() + "/" + serviceName);
336         }
337         if (StringUtils.isNotBlank(namespace))
338         {
339             soapService.getServiceDescription().setDefaultNamespace(namespace);
340         }
341         soapService.init();
342         soapService.stop();
343     }
344 
345     @Override
346     protected void doConnect() throws Exception
347     {
348         // Tell the axis configuration about our new service.
349         connector.getServerProvider().deployService(soapService.getName(), soapService);
350         connector.registerReceiverWithMuleService(this, endpoint.getEndpointURI());
351     }
352 
353     @Override
354     protected void doDisconnect() throws Exception
355     {
356         try
357         {
358             doStop();
359         }
360         catch (MuleException e)
361         {
362             logger.error(e.getMessage(), e);
363         }
364         // TODO: how do you undeploy an Axis service?
365 
366         // Unregister the mule part of the service
367         connector.unregisterReceiverWithMuleService(this, endpoint.getEndpointURI());
368     }
369 
370     @Override
371     protected void doStart() throws MuleException
372     {
373         if (soapService != null)
374         {
375             soapService.start();
376         }
377     }
378 
379     @Override
380     protected void doStop() throws MuleException
381     {
382         if (soapService != null)
383         {
384             soapService.stop();
385         }
386     }
387 
388     @Override
389     protected void doDispose()
390     {
391         // nothing to do
392     }
393 
394     protected void setOptionIfNotset(SOAPService service, String option, Object value)
395     {
396         Object val = service.getOption(option);
397         if (val == null)
398         {
399             service.setOption(option, value);
400         }
401     }
402 
403     public SOAPService getSoapService()
404     {
405         return soapService;
406     }
407 }