View Javadoc

1   package org.codehaus.xfire.service.binding;
2   
3   import java.lang.reflect.Method;
4   import java.lang.reflect.Modifier;
5   import java.net.URL;
6   import java.util.HashSet;
7   import java.util.Iterator;
8   import java.util.Map;
9   import java.util.Set;
10  
11  import javax.wsdl.factory.WSDLFactory;
12  import javax.wsdl.xml.WSDLReader;
13  import javax.xml.namespace.QName;
14  
15  import org.codehaus.xfire.MessageContext;
16  import org.codehaus.xfire.XFireRuntimeException;
17  import org.codehaus.xfire.fault.FaultSender;
18  import org.codehaus.xfire.fault.Soap11FaultSerializer;
19  import org.codehaus.xfire.fault.Soap12FaultSerializer;
20  import org.codehaus.xfire.handler.OutMessageSender;
21  import org.codehaus.xfire.service.MessageInfo;
22  import org.codehaus.xfire.service.OperationInfo;
23  import org.codehaus.xfire.service.Service;
24  import org.codehaus.xfire.service.ServiceFactory;
25  import org.codehaus.xfire.service.ServiceInfo;
26  import org.codehaus.xfire.soap.Soap11;
27  import org.codehaus.xfire.soap.SoapConstants;
28  import org.codehaus.xfire.soap.SoapVersion;
29  import org.codehaus.xfire.transport.TransportManager;
30  import org.codehaus.xfire.util.ClassLoaderUtils;
31  import org.codehaus.xfire.util.NamespaceHelper;
32  import org.codehaus.xfire.util.ServiceUtils;
33  import org.codehaus.xfire.wsdl.ResourceWSDL;
34  import org.codehaus.xfire.wsdl11.WSDL11ParameterBinding;
35  import org.codehaus.xfire.wsdl11.builder.WSDLBuilderAdapter;
36  import org.codehaus.xfire.wsdl11.builder.DefaultWSDLBuilderFactory;
37  import org.codehaus.xfire.wsdl11.builder.WSDLBuilderFactory;
38  
39  /***
40   * Java objects-specific implementation of the {@link ServiceFactory} interface.
41   *
42   * @author <a href="mailto:dan@envoisolutions.com">Dan Diephouse</a>
43   * @author <a href="mailto:poutsma@mac.com">Arjen Poutsma</a>
44   */
45  public class ObjectServiceFactory
46          implements ServiceFactory
47  {
48      private BindingProvider bindingProvider;
49      private TransportManager transportManager;
50      private String style;
51      private String use;
52      private Set ignoredClasses = new HashSet();
53      private SoapVersion soapVersion = Soap11.getInstance();
54      private boolean voidOneWay;
55      private WSDLBuilderFactory wsdlBuilderFactory = new DefaultWSDLBuilderFactory();
56  
57      /***
58       * Initializes a new instance of the <code>ObjectServiceFactory</code>.
59       */
60      public ObjectServiceFactory()
61      {
62          setStyle(SoapConstants.STYLE_WRAPPED);
63          setUse(SoapConstants.USE_LITERAL);
64          ignoredClasses.add("java.lang.Object");
65          ignoredClasses.add("org.omg.CORBA_2_3.portable.ObjectImpl");
66          ignoredClasses.add("org.omg.CORBA.portable.ObjectImpl");
67          ignoredClasses.add("javax.ejb.EJBObject");
68          ignoredClasses.add("javax.rmi.CORBA.Stub");
69      }
70  
71      /***
72       * Initializes a new instance of the <code>ObjectServiceFactory</code> with the given transport manager and type
73       * mapping registry.
74       *
75       * @param transportManager the transport manager
76       * @param provider         the binding provider
77       */
78      public ObjectServiceFactory(TransportManager transportManager, BindingProvider provider)
79      {
80          this();
81  
82          this.bindingProvider = provider;
83          this.transportManager = transportManager;
84      }
85  
86      public ObjectServiceFactory(TransportManager transportManager)
87      {
88          this();
89  
90          this.transportManager = transportManager;
91      }
92  
93      public BindingProvider getBindingProvider()
94      {
95          if (bindingProvider == null)
96          {
97              try
98              {
99                  bindingProvider = (BindingProvider) ClassLoaderUtils
100                         .loadClass("org.codehaus.xfire.aegis.AegisBindingProvider", getClass()).newInstance();
101             }
102             catch (Exception e)
103             {
104                 throw new XFireRuntimeException("Couldn't find a binding provider!", e);
105             }
106         }
107 
108         return bindingProvider;
109     }
110 
111     /***
112      * @param wsdlUrl
113      * @return
114      */
115     public Service create(Class clazz, URL wsdlUrl)
116             throws Exception
117     {
118         WSDLReader reader = WSDLFactory.newInstance().newWSDLReader();
119         reader.readWSDL(wsdlUrl.toString());
120 
121         QName name = ServiceUtils.makeQualifiedNameFromClass(clazz);
122         ServiceInfo serviceInfo = new ServiceInfo(name, clazz);
123         Service endpoint = new Service(serviceInfo);
124 
125         endpoint.setWSDLWriter(new ResourceWSDL(wsdlUrl));
126 
127         // TODO: Bring wsdl configuration functionality back!
128 
129         throw new UnsupportedOperationException("create() isn't working yet.");
130 
131         // return endpoint;
132     }
133 
134     /***
135      * Creates a service from the specified class. The service name will be the 
136      * unqualified class name. The namespace will be based on the package. 
137      * The service will use soap version 1.1, wrapped style, and literal use.
138      * 
139      * @param clazz
140      *            The service class used to populate the operations and
141      *            parameters. If the class is an interface, then the
142      *            implementation class that implements that interface must be
143      *            set via {@link Service#setProperty(String, Object)} with the
144      *            property key being
145      *            {@link org.codehaus.xfire.service.binding.ObjectInvoker#SERVICE_IMPL_CLASS}
146      * @return The service.
147      */
148     public Service create(Class clazz)
149     {
150         return create(clazz, (Map) null);
151     }
152 
153     /***
154      * Creates a service from the specified class. The service name will be the 
155      * unqualified class name. The namespace will be based on the package. 
156      * The service will use soap version 1.1, wrapped style, and literal use.
157      * 
158      * @param clazz
159      *            The service class used to populate the operations and
160      *            parameters. If the class is an interface, then the
161      *            implementation class that implements that interface must be
162      *            set via {@link Service#setProperty(String, Object)} with the
163      *            property key being
164      *            {@link org.codehaus.xfire.service.binding.ObjectInvoker#SERVICE_IMPL_CLASS}
165      * @return The service.
166      */
167     public Service create(Class clazz, Map properties)
168     {
169         return create(clazz, null, null, properties);
170     }
171     /***
172      * Creates a service from the specified class, soap version, style and use. The returned service will have a name
173      * based on the class name, and a namespace based on the class package.
174      * <p/>
175      * Some parameters can be <code>null</code>, and will be replaced with sensible defaults if so. See the specific
176      * parameters for more info.
177      *
178      * @param clazz            The service class used to populate the operations and parameters.
179      * @param name             The name of the service. If <code>null</code>, a name will be generated from the class
180      *                         name.
181      * @param namespace        The default namespace of the service. If <code>null</code>, a namespace will be generated
182      *                         from the class package.
183      * @return The service.
184      */
185     public Service create(Class clazz, String name, String namespace, Map properties)
186     {
187         return create(clazz, name, namespace, null, null, null, properties);
188     }
189 
190     protected String makeServiceNameFromClassName(Class clazz)
191     {
192         return ServiceUtils.makeServiceNameFromClassName(clazz);
193     }
194 
195     public Service create(Class clazz,
196                           String name,
197                           String namespace,
198                           SoapVersion version,
199                           String style,
200                           String use,
201                           Map properties)
202     {
203         String theName = (name != null) ? name : makeServiceNameFromClassName(clazz);
204         String theNamespace = (namespace != null) ? namespace : NamespaceHelper.makeNamespaceFromClassName(
205                 clazz.getName(), "http");
206         QName qName = new QName(theNamespace, theName);
207         SoapVersion theVersion = (version != null) ? version : soapVersion;
208         String theStyle = (style != null) ? style : this.style;
209         String theUse = (use != null) ? use : this.use;
210 
211         ServiceInfo serviceInfo = new ServiceInfo(qName, clazz);
212 
213         Service endpoint = new Service(serviceInfo);
214         setProperties(endpoint, properties);
215         endpoint.setSoapVersion(theVersion);
216 
217         ObjectBinding binding = ObjectBindingFactory.getMessageBinding(theStyle, theUse);
218         binding.setInvoker(new ObjectInvoker());
219         endpoint.setBinding(binding);
220 
221         if (theVersion instanceof Soap11)
222         {
223             endpoint.setFaultSerializer(new Soap11FaultSerializer());
224         }
225         else
226         {
227             endpoint.setFaultSerializer(new Soap12FaultSerializer());
228         }
229 
230         if (transportManager != null && binding instanceof WSDL11ParameterBinding)
231         {
232             endpoint.setWSDLWriter(new WSDLBuilderAdapter(getWsdlBuilderFactory(),
233                                                           endpoint,
234                                                           transportManager,
235                                                           (WSDL11ParameterBinding) binding));
236         }
237 
238         initializeOperations(endpoint);
239 
240         try
241         {
242             BindingProvider provider = getBindingProvider();
243             provider.initialize(endpoint);
244             binding.setBindingProvider(provider);
245         }
246         catch (Exception e)
247         {
248             if(e instanceof XFireRuntimeException) throw (XFireRuntimeException)e;
249             throw new XFireRuntimeException("Couldn't load provider.", e);
250         }
251 
252         registerHandlers(endpoint);
253 
254         return endpoint;
255     }
256 
257     protected void registerHandlers(Service service)
258     {
259         service.addOutHandler(new OutMessageSender());
260         service.addInHandler(service.getBinding());
261         service.addFaultHandler(new FaultSender());
262     }
263 
264     private void setProperties(Service service, Map properties)
265     {
266         if (properties == null) return;
267 
268         for (Iterator itr = properties.entrySet().iterator(); itr.hasNext();)
269         {
270             Map.Entry entry = (Map.Entry) itr.next();
271 
272             service.setProperty((String) entry.getKey(), entry.getValue());
273         }
274     }
275 
276     protected void initializeOperations(Service endpoint)
277     {
278         final Method[] methods = endpoint.getServiceInfo().getServiceClass().getMethods();
279 
280         for (int i = 0; i < methods.length; i++)
281         {
282             final Method method = methods[i];
283 
284             if (isValidMethod(method))
285             {
286                 addOperation(endpoint, method);
287             }
288         }
289     }
290 
291     /***
292      * Ignore the specified class' declared methods. 
293      * This can be used to not expose certain interfaces as a service.
294      * By default, the methods specified by the following interfaces/classes are ignored:
295      * <li><code>java.lang.Object</code>
296      * <li><code>org.omg.CORBA_2_3.portable.ObjectImpl</code>
297      * <li><code>org.omg.CORBA.portable.ObjectImpl</code>
298      * <li><code>javax.ejb.EJBObject</code>
299      * <li><code>javax.ejb.EJBLocalObject</code>
300      * <li><code>javax.rmi.CORBA.Stub</code>
301      * 
302      * @param className the fully qualified class name
303      */
304     public void addIgnoredMethods(String className)
305     {
306         ignoredClasses.add(className);
307     }
308 
309     protected boolean isValidMethod(final Method method)
310     {
311         if(ignoredClasses.contains(method.getDeclaringClass().getName())) return false;
312 
313         final int modifiers = method.getModifiers();
314 
315         return Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers);
316     }
317 
318     protected void addOperation(Service endpoint, final Method method)
319     {
320         ServiceInfo service = endpoint.getServiceInfo();
321         AbstractBinding binding = (AbstractBinding) endpoint.getBinding();
322 
323         final String opName = getOperationName(service, method);
324 
325         final OperationInfo op = service.addOperation(opName, method);
326 
327         op.setAction(getAction(op));
328 
329         final Class[] paramClasses = method.getParameterTypes();
330 
331         final boolean isDoc = binding.getStyle().equals(SoapConstants.STYLE_DOCUMENT);
332 
333         MessageInfo inMsg = op.createMessage(new QName(op.getName() + "Request"));
334         op.setInputMessage(inMsg);
335 
336         for (int j = 0; j < paramClasses.length; j++)
337         {
338             if (isHeader(method, j))
339             {
340                 final QName q = getInParameterName(endpoint, op, method, j, isDoc);
341                 inMsg.addMessageHeader(q, paramClasses[j]).setIndex(j);
342             }
343             else if (!paramClasses[j].equals(MessageContext.class))
344             {
345                 final QName q = getInParameterName(endpoint, op, method, j, isDoc);
346                 inMsg.addMessagePart(q, paramClasses[j]).setIndex(j);
347             }
348         }
349 
350         MessageInfo outMsg = op.createMessage(new QName(op.getName() + "Response"));
351         op.setOutputMessage(outMsg);
352 
353         final Class returnType = method.getReturnType();
354         if (!returnType.isAssignableFrom(void.class))
355         {
356             if (isHeader(method, -1))
357             {
358                 final QName q =  getOutParameterName(endpoint, op, method, isDoc);
359                 outMsg.addMessageHeader(q, method.getReturnType()).setIndex(0);
360             }
361             else
362             {
363                 final QName q = getOutParameterName(endpoint, op, method, isDoc);
364                 outMsg.addMessagePart(q, method.getReturnType());
365             }
366         }
367 
368         op.setMEP(getMEP(method));
369         op.setAsync(isAsync(method));
370     }
371 
372     protected String getAction(OperationInfo op)
373     {
374         return "";
375     }
376 
377     protected boolean isHeader(Method method, int j)
378     {
379         return false;
380     }
381 
382     /***
383      * Creates a name for the operation from the method name. If an operation with that name
384      * already exists, a name is create by appending an integer to the end. I.e. if there is already
385      * two methods named <code>doSomething</code>, the first one will have an operation name of
386      * "doSomething" and the second "doSomething1".
387      * 
388      * @param service
389      * @param method
390      * @return
391      */
392     protected String getOperationName(ServiceInfo service, Method method)
393     {
394         if (service.getOperation(method.getName()) == null)
395         {
396             return method.getName();
397         }
398 
399         int i = 1;
400         while (true)
401         {
402             String name = method.getName() + i;
403             if (service.getOperation(name) == null)
404             {
405                 return name;
406             }
407             else
408             {
409                 i++;
410             }
411         }
412     }
413 
414     protected String getMEP(final Method method)
415     {
416         if (isVoidOneWay() && method.getReturnType().equals(void.class))
417         {
418             return SoapConstants.MEP_IN;
419         }
420         return SoapConstants.MEP_ROBUST_IN_OUT;
421     }
422 
423     protected boolean isAsync(final Method method)
424     {
425         return false;
426     }
427 
428     protected QName getInParameterName(final Service endpoint,
429                                        final OperationInfo op,
430                                        final Method method,
431                                        final int paramNumber,
432                                        final boolean doc)
433     {
434         QName suggestion = getBindingProvider().getSuggestedName(endpoint, op, paramNumber);
435         
436         if (suggestion != null) return suggestion;
437         
438         String paramName = "";
439         if (doc)
440             paramName = method.getName();
441 
442         return new QName(endpoint.getServiceInfo().getName().getNamespaceURI(), paramName + "in" + paramNumber);
443     }
444 
445     protected QName getOutParameterName(final Service endpoint, 
446                                         final OperationInfo op, 
447                                         final Method method, 
448                                         final boolean doc)
449     {
450         QName suggestion = getBindingProvider().getSuggestedName(endpoint, op, -1);
451         
452         if (suggestion != null) return suggestion;
453         
454         String outName = "";
455         if (doc)
456             outName = method.getName();
457 
458         return new QName(endpoint.getServiceInfo().getName().getNamespaceURI(), outName + "out");
459     }
460 
461     public TransportManager getTransportManager()
462     {
463         return transportManager;
464     }
465 
466     public void setTransportManager(TransportManager transportManager)
467     {
468         this.transportManager = transportManager;
469     }
470 
471     public void setBindingProvider(BindingProvider bindingProvider)
472     {
473         this.bindingProvider = bindingProvider;
474     }
475 
476     public String getStyle()
477     {
478         return style;
479     }
480 
481     public void setStyle(String style)
482     {
483         this.style = style;
484     }
485 
486     public String getUse()
487     {
488         return use;
489     }
490 
491     public void setUse(String use)
492     {
493         this.use = use;
494     }
495 
496     public SoapVersion getSoapVersion()
497     {
498         return soapVersion;
499     }
500 
501     public void setSoapVersion(SoapVersion soapVersion)
502     {
503         this.soapVersion = soapVersion;
504     }
505 
506     public boolean isVoidOneWay()
507     {
508         return voidOneWay;
509     }
510 
511     public void setVoidOneWay(boolean voidOneWay)
512     {
513         this.voidOneWay = voidOneWay;
514     }
515 
516     public WSDLBuilderFactory getWsdlBuilderFactory()
517     {
518         return wsdlBuilderFactory;
519     }
520 
521     public void setWsdlBuilderFactory(WSDLBuilderFactory wsdlBuilderFactory)
522     {
523         this.wsdlBuilderFactory = wsdlBuilderFactory;
524     }
525 }