1 package org.codehaus.xfire.util;
2
3 import java.io.InputStream;
4 import java.lang.reflect.Constructor;
5 import java.lang.reflect.Method;
6 import java.util.ArrayList;
7 import java.util.Collection;
8 import java.util.Collections;
9 import java.util.HashMap;
10 import java.util.List;
11 import java.util.Map;
12
13 import javax.xml.stream.XMLInputFactory;
14 import javax.xml.stream.XMLStreamException;
15 import javax.xml.stream.XMLStreamReader;
16
17 import org.apache.commons.logging.Log;
18 import org.apache.commons.logging.LogFactory;
19 import org.codehaus.xfire.XFire;
20 import org.codehaus.xfire.XFireRuntimeException;
21 import org.codehaus.xfire.handler.Handler;
22 import org.codehaus.xfire.service.Service;
23 import org.codehaus.xfire.service.ServiceRegistry;
24 import org.codehaus.xfire.service.binding.BindingProvider;
25 import org.codehaus.xfire.service.binding.ObjectInvoker;
26 import org.codehaus.xfire.service.binding.ObjectServiceFactory;
27 import org.codehaus.xfire.soap.Soap11;
28 import org.codehaus.xfire.soap.Soap12;
29 import org.codehaus.xfire.soap.SoapVersion;
30 import org.codehaus.xfire.transport.TransportManager;
31 import org.codehaus.yom.Document;
32 import org.codehaus.yom.Element;
33 import org.codehaus.yom.Elements;
34 import org.codehaus.yom.stax.StaxBuilder;
35
36 /***
37 * Builds services from an xml configuration file.
38 *
39 * @author <a href="mailto:dan@envoisolutions.com">Dan Diephouse</a>
40 */
41 public class XMLServiceBuilder
42 {
43 private static final Log log = LogFactory.getLog(XMLServiceBuilder.class);
44
45 private static final Object DEFAULT_FACTORY_INSTANCE = new DefaultFactory();
46
47 private static Method DEFAULT_FACTORY_METHOD = null;
48
49 /***
50 * It's likely that the same factory object will be used for creation for more then only 1 object, so cache it.
51 */
52 private Map factoryCache = new HashMap();
53
54 private XFire xfire;
55
56 public XMLServiceBuilder(XFire xfire)
57 {
58 this.xfire = xfire;
59 try
60 {
61 DEFAULT_FACTORY_METHOD = DefaultFactory.class.getMethod("create",new Class[]{String.class});
62 }
63 catch (SecurityException e)
64 {
65
66 log.error(e);
67 }
68 catch (NoSuchMethodException e)
69 {
70
71 log.error(e);
72 }
73 }
74
75 protected XFire getXFire()
76 {
77 return xfire;
78 }
79
80 /***
81 * Returns a collection of SOAP services.
82 * <p>
83 * This method takes an input stream and for each service element
84 * builds a SOAP service. The stream is interrogated for the following
85 * element values: name, namespace, style, use, serviceClass,
86 * implementationClass, bindingProvider, and
87 * property (a repeatable element) using attribute 'key'
88 * <p>
89 * @param stream A xml-based resource bundle
90 * @return Collection A collection of services prescribed in the bundle
91 */
92 public Collection buildServices(InputStream stream)
93 throws Exception
94 {
95 try
96 {
97 XMLInputFactory ifactory = XMLInputFactory.newInstance();
98 XMLStreamReader reader = ifactory.createXMLStreamReader(stream);
99 StaxBuilder builder = new StaxBuilder();
100 Document doc = builder.build(reader);
101 Element root = doc.getRootElement();
102
103 List serviceList = new ArrayList();
104 Elements contents = root.getChildElements("services");
105 for (int i = 0; i < contents.size(); i++)
106 {
107 Element element = contents.get(i);
108 Elements services = element.getChildElements();
109 for (int n = 0; n < services.size(); n++)
110 {
111 Element service = services.get(n);
112
113 serviceList.add(loadService(service));
114 }
115 }
116 return serviceList;
117 }
118 catch (XMLStreamException e1)
119 {
120 log.error("Could not parse META-INF/xfire/services.xml!", e1);
121 throw e1;
122 }
123 }
124
125 /***
126 * Keeps data about object factory.
127 * @author <a href="mailto:tsztelak@gmail.com">Tomasz Sztelak</a>
128 *
129 */
130 private class ObjectFactory
131 {
132 /***
133 * Instance of object declared as factory.
134 */
135 private Object factoryInstance;
136
137 /***
138 * Method declared as factory method.
139 */
140 private Method factoryMethod;
141
142 /***
143 * @param factory
144 * @param method
145 */
146 public ObjectFactory(final Object factory,final Method method){
147 factoryInstance = factory;
148 factoryMethod = method;
149 }
150
151 /***
152 * Creates object given by className param using factory object instance @see factoryInstance.
153 * @param className
154 * @return created object.
155 * @throws Exception
156 */
157 public Object createObject(String className)
158 throws Exception
159 {
160 try{
161 return factoryMethod.invoke(factoryInstance, new Object[] { className });
162 }catch(Exception e){
163 throw new XFireRuntimeException("Coundn't create instance of object :"+className,e);
164 }
165 }
166
167
168 }
169
170 /***
171 * Returns ObjectFactory object created from values defined as attribues for given Element or null if no required
172 * attributes are not present.
173 * @param element
174 * @return
175 * @throws Exception
176 */
177 private ObjectFactory getObjectFactory(Element element)
178 throws Exception
179 {
180
181 Method factoryMethod = null;
182 Object factoryInstance = null;
183
184 String factoryClassName = element.getAttributeValue("factory-class");
185 String factoryMethodName = element.getAttributeValue("factory-method");
186
187
188 if (factoryClassName != null && factoryClassName.length() > 0 && factoryMethodName != null
189 && factoryMethodName.length() > 0)
190 {
191
192
193 factoryInstance = factoryCache.get(factoryClassName);
194 if (factoryInstance == null)
195 {
196 factoryInstance = loadClass(factoryClassName).newInstance();
197 factoryCache.put(factoryClassName, factoryInstance);
198 }
199
200 factoryMethod = factoryInstance.getClass().getMethod(factoryMethodName,new Class[]{String.class});
201
202 }else{
203
204
205 factoryInstance = DEFAULT_FACTORY_INSTANCE;
206 factoryMethod = DEFAULT_FACTORY_METHOD;
207 }
208
209 return new ObjectFactory(factoryInstance, factoryMethod);
210 }
211
212 protected Service loadService(Element service)
213 throws Exception
214 {
215 ServiceRegistry registry = getXFire().getServiceRegistry();
216
217 String name = getElementValue(service, "name", null);
218 String namespace = getElementValue(service, "namespace", null);
219 String style = getElementValue(service, "style", "");
220 String use = getElementValue(service, "use", "");
221 String serviceClass = getElementValue(service, "serviceClass", "");
222 String implClassName = getElementValue(service, "implementationClass", "");
223 String bindingProviderName = getElementValue(service, "bindingProvider", "");
224
225 String soapVersionValue = getElementValue(service, "soapVersion", "1.1");
226 SoapVersion soapVersion;
227 if (soapVersionValue.equals("1.2"))
228 {
229 soapVersion = Soap12.getInstance();
230 }
231 else
232 {
233 soapVersion = Soap11.getInstance();
234 }
235
236 Class clazz = null;
237 try
238 {
239 clazz = loadClass(serviceClass);
240 }
241 catch (Exception e)
242 {
243 throw new XFireRuntimeException("Could not load service class: " + serviceClass, e);
244 }
245
246 BindingProvider bindingProvider = loadBindingProvider(bindingProviderName);
247
248 String serviceFactory = getElementValue(service, "serviceFactory", "");
249 ObjectServiceFactory factory;
250 if (serviceFactory.equals("jsr181") || serviceFactory.equals("commons-attributes"))
251 factory = getAnnotationServiceFactory(serviceFactory, bindingProvider);
252 else
253 factory = loadServiceFactory(bindingProvider, serviceFactory);
254
255 if (style.length() > 0)
256 factory.setStyle(style);
257 if (use.length() > 0)
258 factory.setUse(use);
259
260 factory.setSoapVersion(soapVersion);
261
262 Service svc = null;
263 if (name != null || namespace != null)
264 {
265 svc = factory.create(clazz, name, namespace, null);
266 }
267 else
268 {
269 svc = factory.create(clazz);
270 }
271
272 if (implClassName.length() > 0)
273 {
274 Class implClazz = null;
275 try
276 {
277 implClazz = loadClass(implClassName);
278 }
279 catch (Exception e)
280 {
281 throw new XFireRuntimeException("Could not load implementation class: "
282 + serviceClass, e);
283 }
284
285 svc.setProperty(ObjectInvoker.SERVICE_IMPL_CLASS, implClazz);
286
287 if (log.isInfoEnabled())
288 {
289 log.info("Created Service " + name + " with impl " + implClazz + ", soap version: "
290 + soapVersionValue + ", style: " + style + ", use: " + use + ", namespace "
291 + svc.getServiceInfo().getName().getNamespaceURI());
292 }
293 }
294 else
295 {
296 if (log.isInfoEnabled())
297 {
298 log.info("Created Service " + name + " with impl " + clazz + ", soap version: "
299 + soapVersionValue + ", style: " + style + ", use: " + use + ", namespace "
300 + svc.getServiceInfo().getName().getNamespaceURI());
301 }
302 }
303
304 loadServiceProperties(svc,service);
305
306 svc.getInHandlers().addAll(createHandlers(service.getFirstChildElement("inHandlers")));
307 svc.getOutHandlers().addAll(createHandlers(service.getFirstChildElement("outHandlers")));
308 svc.getFaultHandlers().addAll(createHandlers(service.getFirstChildElement("faultHandlers")));
309
310 registry.register(svc);
311
312 return svc;
313 }
314
315 protected ObjectServiceFactory loadServiceFactory(BindingProvider bindingProvider,
316 String serviceFactoryName)
317 {
318 ObjectServiceFactory factory = null;
319 if (serviceFactoryName.length() > 0)
320 {
321
322 try
323 {
324 Class clz = loadClass(serviceFactoryName);
325 TransportManager tman = getXFire().getTransportManager();
326
327 Constructor con = null;
328 Object[] arguments = null;
329
330 try
331 {
332 con = clz.getConstructor(new Class[] { TransportManager.class,
333 BindingProvider.class });
334 arguments = new Object[] { tman, bindingProvider };
335 }
336 catch (NoSuchMethodException e)
337 {
338 try
339 {
340 con = clz.getConstructor(new Class[] { TransportManager.class });
341 arguments = new Object[] { tman };
342 }
343 catch (NoSuchMethodException e1)
344 {
345 con = clz.getConstructor(new Class[0]);
346 arguments = new Object[0];
347 }
348 }
349
350 return (ObjectServiceFactory) con.newInstance(arguments);
351 }
352 catch (Exception e)
353 {
354 throw new XFireRuntimeException("Could not load service factory: "
355 + serviceFactoryName, e);
356 }
357 }
358 else
359 {
360 factory = new ObjectServiceFactory(getXFire().getTransportManager(), bindingProvider);
361 }
362
363 return factory;
364 }
365
366 protected ObjectServiceFactory getAnnotationServiceFactory(String annotationType,
367 BindingProvider bindingProvider)
368 throws Exception
369 {
370 Class annotsClz = null;
371 Class clz = loadClass("org.codehaus.xfire.annotations.AnnotationServiceFactory");
372
373 if (annotationType.equals("jsr181"))
374 {
375 annotsClz = loadClass("org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations");
376 }
377 else if (annotationType.equals("commons-attributes"))
378 {
379 annotsClz = loadClass("org.codehaus.xfire.annotations.commons.CommonsWebAttributes");
380 }
381
382 Class webAnnot = loadClass("org.codehaus.xfire.annotations.WebAnnotations");
383
384 Constructor con = clz.getConstructor(new Class[] { webAnnot, TransportManager.class,
385 BindingProvider.class });
386
387 return (ObjectServiceFactory) con.newInstance(new Object[] { annotsClz.newInstance(),
388 getXFire().getTransportManager(), bindingProvider });
389 }
390
391 protected BindingProvider loadBindingProvider(String bindingProviderName)
392 {
393 BindingProvider bindingProvider = null;
394 if (bindingProviderName.length() > 0)
395 {
396 try
397 {
398 bindingProvider = (BindingProvider) loadClass(bindingProviderName).newInstance();
399 }
400 catch (Exception e)
401 {
402 throw new XFireRuntimeException("Could not load binding provider: "
403 + bindingProvider, e);
404 }
405 }
406 return bindingProvider;
407 }
408
409 private List createHandlers(Element child)
410 throws Exception
411 {
412 if (child == null)
413 return Collections.EMPTY_LIST;
414
415 Elements handlers = child.getChildElements("handler");
416 if (handlers.size() == 0)
417 return Collections.EMPTY_LIST;
418
419 List pipe = new ArrayList();
420
421 for (int i = 0; i < handlers.size(); i++)
422 {
423 pipe.add(getHandler(handlers.get(i)));
424 }
425
426 return pipe;
427 }
428
429 /***
430 * @param element
431 * @return
432 * @throws Exception
433 */
434 protected Handler getHandler(Element element)
435 throws Exception
436 {
437 String handlerClassName = element.getValue();
438 return (Handler) getObjectFactory(element).createObject(handlerClassName);
439 }
440
441 public String getElementValue(Element root, String name, String def)
442 {
443 Element child = root.getFirstChildElement(name);
444 if (child != null)
445 {
446 String value = child.getValue();
447 if (value != null && value.length() > 0)
448 return value;
449 }
450
451 return def;
452 }
453
454 private void loadServiceProperties(Service svc, Element child)
455 {
456 Elements elements = child.getChildElements("property");
457 if (elements.size() == 0)
458 return;
459 for (int i = 0; i < elements.size(); i++)
460 {
461 Element element = (Element) elements.get(i);
462 String key = element.getAttributeValue("key");
463 String value = element.getValue();
464 if (key.length() > 1 && value.length() > 1)
465 svc.setProperty(key, value);
466 }
467 }
468
469 /***
470 * Load a class from the class loader.
471 *
472 * @param className
473 * The name of the class.
474 * @return The class.
475 * @throws Exception
476 */
477 protected Class loadClass(String className)
478 throws Exception
479 {
480
481 if (className.endsWith("[]"))
482 {
483 className = "[L" + className.substring(0, className.length() - 2) + ";";
484 }
485
486 return ClassLoaderUtils.loadClass(className, getClass());
487 }
488
489 private static class DefaultFactory
490 {
491 public Object create(String className)
492 throws Exception
493 {
494
495 if (className.endsWith("[]"))
496 {
497 className = "[L" + className.substring(0, className.length() - 2) + ";";
498 }
499
500 return ClassLoaderUtils.loadClass(className, getClass()).newInstance();
501
502 }
503 }
504 }
505