View Javadoc

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