View Javadoc

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              //  Imposible :)
66              log.error(e);
67          }
68          catch (NoSuchMethodException e)
69          {
70              // Imposible :)
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         // Both factoryClassName and factoryNethodName must be provided.
188         if (factoryClassName != null && factoryClassName.length() > 0 && factoryMethodName != null
189                 && factoryMethodName.length() > 0)
190         {
191 
192             // Check if factory instance was created before..
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             // No data for factory is provied so use default to avoid if(factory == null ) new MyClass() code
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             // Attempt to load a ServiceFactory for the user.
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         // Handle array'd types.
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             // Handle array'd types.
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