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