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
128
129 throw new UnsupportedOperationException("create() isn't working yet.");
130
131
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 }