KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > easybeans > xmlconfig > XMLConfiguration


1 /**
2  * EasyBeans
3  * Copyright (C) 2006 Bull S.A.S.
4  * Contact: easybeans@objectweb.org
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  * USA
20  *
21  * --------------------------------------------------------------------------
22  * $Id: XMLConfiguration.java 1126 2006-09-27 13:54:56Z benoitf $
23  * --------------------------------------------------------------------------
24  */

25
26 package org.objectweb.easybeans.xmlconfig;
27
28 import java.lang.reflect.InvocationTargetException JavaDoc;
29 import java.lang.reflect.Method JavaDoc;
30 import java.lang.reflect.ParameterizedType JavaDoc;
31 import java.lang.reflect.Type JavaDoc;
32 import java.net.URL JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.HashMap JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.Map JavaDoc;
37
38 import org.objectweb.easybeans.log.JLog;
39 import org.objectweb.easybeans.log.JLogFactory;
40 import org.objectweb.easybeans.util.xml.DocumentParser;
41 import org.objectweb.easybeans.util.xml.DocumentParserException;
42 import org.objectweb.easybeans.xmlconfig.mapping.AttributeMapping;
43 import org.objectweb.easybeans.xmlconfig.mapping.ClassMapping;
44 import org.objectweb.easybeans.xmlconfig.mapping.XMLMapping;
45 import org.objectweb.easybeans.xmlconfig.mapping.XMLMappingBuilder;
46 import org.w3c.dom.Document JavaDoc;
47 import org.w3c.dom.Element JavaDoc;
48 import org.w3c.dom.NamedNodeMap JavaDoc;
49 import org.w3c.dom.Node JavaDoc;
50 import org.w3c.dom.NodeList JavaDoc;
51
52 /**
53  * XML configuration class that configure a given object with an xml
54  * configuration file. <br>
55  * Additional information will be searched on the namespace with xxx-mapping.xml
56  * file with xxx the name of the element defining the namespace.
57  * @author Florent Benoit
58  */

59 public class XMLConfiguration {
60
61     /**
62      * Http:// namespace.
63      */

64     private static final String JavaDoc HTTP_NAMESPACE = "http://";
65
66     /**
67      * By default, no validation.
68      */

69     private static final boolean VALIDATING_OFF = false;
70
71     /**
72      * Suffix for mapping file.
73      */

74     private static final String JavaDoc MAPPING_SUFFIX = "-mapping.xml";
75
76     /**
77      * Logger.
78      */

79     private JLog logger = JLogFactory.getLog(XMLConfiguration.class);
80
81     /**
82      * Validating when parsing xml ?
83      */

84     private boolean validating = VALIDATING_OFF;
85
86     /**
87      * URL of the configuration file.
88      */

89     private URL JavaDoc xmlConfigurationURL;
90
91     /**
92      * Mapping between namespace and XMLMapping object.
93      */

94     private Map JavaDoc<String JavaDoc, XMLMapping> mappings = null;
95
96     /**
97      * Link between name and the objects.
98      */

99     private HashMap JavaDoc<String JavaDoc, Object JavaDoc> configuredElements = null;
100
101
102     /**
103      * Build an xml configuration with the given xml configuration file.
104      * @param xmlConfigurationURL the given xml configuration file
105      */

106     public XMLConfiguration(final URL JavaDoc xmlConfigurationURL) {
107         this.xmlConfigurationURL = xmlConfigurationURL;
108         this.mappings = new HashMap JavaDoc<String JavaDoc, XMLMapping>();
109         configuredElements = new HashMap JavaDoc<String JavaDoc, Object JavaDoc>();
110     }
111
112     /**
113      * Configure the given object with the configuration that was analyzed.
114      * @param object the object to configure.
115      * @throws XMLConfigurationException if configuration fails
116      */

117     public void configure(final Object JavaDoc object) throws XMLConfigurationException {
118         Element JavaDoc rootElement = analyzeXmlFile();
119         configure(object, rootElement);
120     }
121
122     /**
123      * Gets the root element of the XML parsed file.
124      * @return the element object
125      * @throws XMLConfigurationException if analyze fails.
126      */

127     private Element JavaDoc analyzeXmlFile() throws XMLConfigurationException {
128         // Parse the XML file and build all configuration process.
129
Document JavaDoc xmlConfigurationDocument = null;
130         try {
131             xmlConfigurationDocument = DocumentParser.getDocument(xmlConfigurationURL, validating, null);
132         } catch (DocumentParserException e) {
133             throw new XMLConfigurationException("Cannot get a document on the given url '" + xmlConfigurationURL + "'.", e);
134         }
135
136         // Get the root element
137
Element JavaDoc rootElement = xmlConfigurationDocument.getDocumentElement();
138
139         // get namespace of the element
140
String JavaDoc nameSpace = rootElement.getNamespaceURI();
141         // and the name
142
String JavaDoc nodeName = rootElement.getNodeName();
143
144         // init mapping for the element
145
getXMLMapping(nameSpace, nodeName);
146
147         return rootElement;
148
149     }
150
151     /**
152      * Configure the given object by looking at the element (of the configuration).
153      * @param object the given object to configure.
154      * @param element the element that contains configuration.
155      * @throws XMLConfigurationException if configuration fails
156      */

157     public void configure(final Object JavaDoc object, final Element JavaDoc element) throws XMLConfigurationException {
158
159         // Get all children of the given element
160
NodeList JavaDoc nodeList = element.getChildNodes();
161
162         // For each element, create object and configure it
163
int length = nodeList.getLength();
164         for (int i = 0; i < length; i++) {
165             Node JavaDoc node = nodeList.item(i);
166
167             // Get an element, create an instance of the element
168
if (Node.ELEMENT_NODE == node.getNodeType()) {
169                 ClassMapping classMapping = getClassMapping(node.getNamespaceURI(), node.getNodeName());
170
171                 Object JavaDoc newObject = newInstance(node, classMapping);
172
173                 // Set attributes
174
NamedNodeMap JavaDoc attributes = ((Element JavaDoc) node).getAttributes();
175                 int attributesLength = attributes.getLength();
176                 for (int a = 0; a < attributesLength; a++) {
177                     Node JavaDoc nodeAttribute = attributes.item(a);
178                     if (nodeAttribute.getNamespaceURI() == null) {
179                         String JavaDoc propertyName = nodeAttribute.getNodeName();
180                         String JavaDoc propertyValue = nodeAttribute.getNodeValue();
181                         setAttribute(newObject, propertyName, propertyValue, classMapping);
182                     }
183                 }
184                 // add or set element on the given object
185
addSetElement(object, newObject);
186
187                 // Store element
188
configuredElements.put(node.getNodeName(), newObject);
189
190                 // configure sub element of the new object.
191
configure(newObject, (Element JavaDoc) node);
192             }
193         }
194     }
195
196     /**
197      * Gets the Mapping for the given nameSpaceURI and the given classname.
198      * @param nameSpaceURI the namespace of the xml element
199      * @param name the name of the class
200      * @return the class mapping object
201      * @throws XMLConfigurationException if no configuration is found
202      */

203     private ClassMapping getClassMapping(final String JavaDoc nameSpaceURI, final String JavaDoc name) throws XMLConfigurationException {
204         // Get classname with the namespace and alias
205
XMLMapping xmlMapping = mappings.get(nameSpaceURI);
206         if (xmlMapping == null) {
207             throw new XMLConfigurationException("No mapping found for namespace '" + nameSpaceURI + "'.");
208         }
209
210         // alias present ?
211
ClassMapping classMapping = xmlMapping.getClassMapping(name);
212
213         // no mapping
214
if (classMapping == null) {
215             throw new XMLConfigurationException("No class found for element '" + name + "' withing xmlmapping '" + xmlMapping
216                     + "'.");
217         }
218         return classMapping;
219     }
220
221     /**
222      * Build class instance for the given node.
223      * @param node the element of the xml
224      * @param classMapping the mapping between node name and class name
225      * @return the instance of the object
226      * @throws XMLConfigurationException if no new instance can be done.
227      */

228     private Object JavaDoc newInstance(final Node JavaDoc node, final ClassMapping classMapping) throws XMLConfigurationException {
229         // get classname
230
String JavaDoc className = classMapping.getName();
231
232         // load class
233
Class JavaDoc clazz;
234         try {
235             clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
236         } catch (ClassNotFoundException JavaDoc e) {
237             throw new XMLConfigurationException("Cannot load the class '" + className + "'.");
238         }
239         logger.debug("Building instance of the class {0}", className);
240         // build a new instance
241
try {
242             return clazz.newInstance();
243         } catch (InstantiationException JavaDoc e) {
244             throw new XMLConfigurationException("Cannot build an instance of class '" + className + "'.", e);
245         } catch (IllegalAccessException JavaDoc e) {
246             throw new XMLConfigurationException("Cannot build an instance of class '" + className + "'.", e);
247         }
248     }
249
250     /**
251      * Set attribute on the given object.
252      * @param configuringObject the object to configure
253      * @param propertyName the name of the property (for the setter)
254      * @param propertyValue the value of the property
255      * @param classMapping the mapping (if there are attributes aliasing)
256      * @throws XMLConfigurationException if attribute cannot be set.
257      */

258     private void setAttribute(final Object JavaDoc configuringObject, final String JavaDoc propertyName, final String JavaDoc propertyValue,
259             final ClassMapping classMapping) throws XMLConfigurationException {
260
261         // get class of the configuringObject
262
Class JavaDoc configuringClass = configuringObject.getClass();
263
264         // Get methods
265
Method JavaDoc[] methods = configuringClass.getMethods();
266
267         String JavaDoc updatedPropertyName = propertyName;
268
269         // alias ?
270
AttributeMapping attributeMapping = classMapping.getAttributeMapping(propertyName);
271         if (attributeMapping != null) {
272             String JavaDoc name = attributeMapping.getName();
273             if (name != null) {
274                 updatedPropertyName = name;
275             }
276         }
277
278         String JavaDoc setterName = "set" + updatedPropertyName.substring(0, 1).toUpperCase() + updatedPropertyName.substring(1);
279
280         Object JavaDoc paramObject = null;
281         // Search setter
282
Method JavaDoc findedMethod = null;
283         if (methods != null) {
284             for (Method JavaDoc m : methods) {
285                 // set or add ?
286
if (m.getName().startsWith(setterName)) {
287                     // only one arg ?
288
Class JavaDoc[] parameters = m.getParameterTypes();
289                     if (parameters.length == 1) {
290                         findedMethod = m;
291                         Class JavaDoc parameterClass = parameters[0];
292
293                         // Special case of reference
294
if (propertyValue.startsWith("#") && propertyValue.length() >= 2) {
295                             paramObject = configuredElements.get(propertyValue.substring(1));
296                         } else if (String JavaDoc.class.equals(parameterClass)) {
297                             paramObject = propertyValue;
298                         } else if (Integer.TYPE.equals(parameterClass)) {
299                             paramObject = new Integer JavaDoc(propertyValue);
300                         } else if (Boolean.TYPE.equals(parameterClass)) {
301                             paramObject = new Boolean JavaDoc(propertyValue);
302                         }
303
304                         break;
305                     }
306                 }
307             }
308         }
309
310         // build
311
if (findedMethod != null && paramObject != null) {
312             logger.debug("Calling {0} on object {1} with value {2}", setterName, configuringObject, paramObject);
313             try {
314                 findedMethod.invoke(configuringObject, paramObject);
315             } catch (IllegalArgumentException JavaDoc e) {
316                 // TODO Auto-generated catch block
317
e.printStackTrace();
318             } catch (IllegalAccessException JavaDoc e) {
319                 // TODO Auto-generated catch block
320
e.printStackTrace();
321             } catch (InvocationTargetException JavaDoc e) {
322                 // TODO Auto-generated catch block
323
e.printStackTrace();
324             }
325         } else {
326
327             throw new XMLConfigurationException("No setter method found for parameter '" + propertyName + "' on class '"
328                     + configuringClass + "'.");
329
330         }
331
332     }
333
334     /**
335      * Finds the add(Class c) or set(Class c) methods.<br>
336      * The parameter needs to be a single entry and not a list.
337      * @param configuringObject the object on which call this setter method.
338      * @param paramObject the parameter object
339      * @return the found method if any.
340      */

341     private Method JavaDoc findSingleSetterMethod(final Object JavaDoc configuringObject, final Object JavaDoc paramObject) {
342         Method JavaDoc setterMethod = null;
343
344         // get class of the configuringObject
345
Class JavaDoc configuringClass = configuringObject.getClass();
346
347         // Get methods of the class
348
Method JavaDoc[] methods = configuringClass.getMethods();
349
350         // class of the new object
351
Class JavaDoc parameterClass = paramObject.getClass();
352         String JavaDoc className = parameterClass.getSimpleName();
353
354         // Search setter
355
if (methods != null) {
356             for (Method JavaDoc m : methods) {
357                 // set or add ?
358
if (m.getName().startsWith("set" + className) || m.getName().startsWith("add" + className)) {
359                     // only one arg ?
360
Class JavaDoc[] parameters = m.getParameterTypes();
361                     if (parameters.length == 1) {
362                         // valid parameter
363
if (parameters[0].equals(parameterClass)) {
364                             setterMethod = m;
365                             break;
366                         }
367                     }
368                 }
369             }
370         }
371         return setterMethod;
372     }
373
374     /**
375      * Finds the get(List&lt;Class&gt;) and set(List&lt;Class&gt;) methods. The parameter
376      * needs to be a list with generics.
377      * @param configuringObject the object on which call this setter method.
378      * @param paramObject the parameter object
379      * @return the getter and setter method with List type.
380      */

381     private Method JavaDoc[] findListGetterSetterMethod(final Object JavaDoc configuringObject, final Object JavaDoc paramObject) {
382
383         // get class of the configuringObject
384
Class JavaDoc configuringClass = configuringObject.getClass();
385
386         // Get methods of the class
387
Method JavaDoc[] methods = configuringClass.getMethods();
388
389         // class of the new object
390
Class JavaDoc parameterClass = paramObject.getClass();
391
392         // Search with class of object
393
String JavaDoc className = parameterClass.getSimpleName();
394         // Suffix name (many items)
395
String JavaDoc argName = className + "s";
396         Method JavaDoc[] searchMethods = searchListGetterSetterMethod(methods, argName, parameterClass);
397
398         return searchMethods;
399     }
400
401     /**
402      * Search getter and setter method in the given set of methods. The getter/setter are used for a List parameter/return type.
403      * @param methods the set of methods to search in
404      * @param argName the name of the argument
405      * @param parameterClass the parameter class (for the generics on the List)
406      * @return an array of methods, array[0] = getter, array[1] = setter.
407      */

408     private Method JavaDoc[] searchListGetterSetterMethod(final Method JavaDoc[] methods, final String JavaDoc argName, final Class JavaDoc parameterClass) {
409         Method JavaDoc[] returnedMethods = null;
410
411         Method JavaDoc getterMethod = null;
412         Method JavaDoc setterMethod = null;
413         // Search getter and setter
414
if (methods != null) {
415             for (Method JavaDoc m : methods) {
416                 // setter
417
if (m.getName().startsWith("set" + argName)) {
418                     // only one arg ?
419
Class JavaDoc[] parameters = m.getParameterTypes();
420
421                     // setter
422
if (parameters.length == 1) {
423                         // ensure it is a List type
424
if (parameters[0].equals(List JavaDoc.class)) {
425                             Type JavaDoc[] types = m.getGenericParameterTypes();
426                             // look only the first type if any
427
if (types.length == 1) {
428                                 if (types[0] instanceof ParameterizedType JavaDoc) {
429                                     ParameterizedType JavaDoc parameterizedType = (ParameterizedType JavaDoc) types[0];
430                                     Type JavaDoc[] typeArguments = parameterizedType.getActualTypeArguments();
431                                     if (typeArguments.length == 1) {
432                                         if (typeArguments[0].equals(parameterClass)) {
433                                             // found it !
434
setterMethod = m;
435                                         }
436                                     }
437
438                                 }
439                             }
440                         }
441                     }
442                 } else if (m.getName().startsWith("get" + argName)) {
443                     Class JavaDoc returnTypeClass = m.getReturnType();
444                     if (returnTypeClass != null && returnTypeClass.equals(List JavaDoc.class)) {
445
446                         // no parameter
447
Class JavaDoc[] parameters = m.getParameterTypes();
448                         if (parameters.length == 0) {
449                             Type JavaDoc type = m.getGenericReturnType();
450                             if (type instanceof ParameterizedType JavaDoc) {
451                                 ParameterizedType JavaDoc parameterizedType = (ParameterizedType JavaDoc) type;
452                                 Type JavaDoc[] typeArguments = parameterizedType.getActualTypeArguments();
453                                 if (typeArguments.length == 1) {
454                                     if (typeArguments[0].equals(parameterClass)) {
455                                         // found it !
456
getterMethod = m;
457                                     }
458                                 }
459                             }
460                         }
461                     }
462                 }
463             }
464         }
465         // try with interfaces of the current class
466
Class JavaDoc[] interfaces = parameterClass.getInterfaces();
467         if (interfaces != null) {
468             for (Class JavaDoc itf : interfaces) {
469                 String JavaDoc itfName = itf.getSimpleName();
470                 String JavaDoc argItfName = itfName + "s";
471                 returnedMethods = searchListGetterSetterMethod(methods, argItfName, itf);
472                 if (returnedMethods != null) {
473                     break;
474                 }
475             }
476         }
477
478         if (getterMethod != null && setterMethod != null) {
479             returnedMethods = new Method JavaDoc[2];
480             returnedMethods[0] = getterMethod;
481             returnedMethods[1] = setterMethod;
482         }
483         return returnedMethods;
484     }
485
486     /**
487      * Add or set element on the given object.
488      * @param configuringObject the object to configure
489      * @param newObject the object to add/set on the configuringObject object
490      * @throws XMLConfigurationException if newObject cannot be set.
491      */

492     @SuppressWarnings JavaDoc("unchecked")
493     private void addSetElement(final Object JavaDoc configuringObject, final Object JavaDoc newObject) throws XMLConfigurationException {
494         Method JavaDoc findedMethod = findSingleSetterMethod(configuringObject, newObject);
495         // single setter ?
496
if (findedMethod != null) {
497             logger.debug("Calling method {0} on object {1} with value {2}", findedMethod, configuringObject, newObject);
498             try {
499                 findedMethod.invoke(configuringObject, newObject);
500             } catch (IllegalArgumentException JavaDoc e) {
501                 // TODO Auto-generated catch block
502
e.printStackTrace();
503             } catch (IllegalAccessException JavaDoc e) {
504                 // TODO Auto-generated catch block
505
e.printStackTrace();
506             } catch (InvocationTargetException JavaDoc e) {
507                 // TODO Auto-generated catch block
508
e.printStackTrace();
509             }
510         } else {
511             // try a list getter/setter
512
Method JavaDoc[] getterAndSetter = findListGetterSetterMethod(configuringObject, newObject);
513             if (getterAndSetter != null) {
514                 Method JavaDoc getterMethod = getterAndSetter[0];
515                 Method JavaDoc setterMethod = getterAndSetter[1];
516
517                 // Get current list
518
List JavaDoc currentList = null;
519                 try {
520                     logger.debug("Calling method {0} on object {1}", getterMethod, configuringObject);
521                     currentList = (List JavaDoc) getterMethod.invoke(configuringObject);
522                 } catch (IllegalArgumentException JavaDoc e) {
523                     // TODO Auto-generated catch block
524
e.printStackTrace();
525                 } catch (IllegalAccessException JavaDoc e) {
526                     // TODO Auto-generated catch block
527
e.printStackTrace();
528                 } catch (InvocationTargetException JavaDoc e) {
529                     // TODO Auto-generated catch block
530
e.printStackTrace();
531                 }
532
533                 // Null list, create one
534
if (currentList == null) {
535                     currentList = new ArrayList JavaDoc();
536                 }
537
538                 // add element
539
logger.debug("Adding element {0} to the list {1}", newObject, currentList);
540                 currentList.add(newObject);
541
542                 // set list
543
try {
544                     logger.debug("Calling method {0} on object {1} with value {2}", setterMethod, configuringObject, currentList);
545                     setterMethod.invoke(configuringObject, currentList);
546                 } catch (IllegalArgumentException JavaDoc e) {
547                     // TODO Auto-generated catch block
548
e.printStackTrace();
549                 } catch (IllegalAccessException JavaDoc e) {
550                     // TODO Auto-generated catch block
551
e.printStackTrace();
552                 } catch (InvocationTargetException JavaDoc e) {
553                     // TODO Auto-generated catch block
554
e.printStackTrace();
555                 }
556
557             } else {
558                 try {
559                     throw new Exception JavaDoc("No setter method found for parameter '" + configuringObject.getClass() + "' on class '"
560                             + newObject.getClass() + "'.");
561                 } catch (Exception JavaDoc e) {
562                     // TODO Auto-generated catch block
563
e.printStackTrace();
564                 }
565             }
566         }
567
568     }
569
570     /**
571      * Gets the XML mapping for the given namespace and the node name.
572      * @param nameSpace current XML namespace.
573      * @param nodeName the name of the node.
574      * @return the XML mapping object.
575      * @throws XMLConfigurationException if xml mapping is not found.
576      */

577     private XMLMapping getXMLMapping(final String JavaDoc nameSpace, final String JavaDoc nodeName) throws XMLConfigurationException {
578         // already one ?
579
if (mappings.containsKey(nameSpace)) {
580             return mappings.get(nameSpace);
581         }
582
583         XMLMapping xmlMapping = null;
584         // With the namespace, get the package name for searching the
585
// <nodeName>-mapping.xml file
586
if (nameSpace != null) {
587             if (nameSpace.startsWith(HTTP_NAMESPACE)) {
588                 // get package
589
String JavaDoc packageName = nameSpace.substring(HTTP_NAMESPACE.length());
590                 // Compute node name and suffix
591
String JavaDoc resourceName = packageName.replaceAll("\\.", "/") + "/" + nodeName + MAPPING_SUFFIX;
592
593                 URL JavaDoc mappingURL = Thread.currentThread().getContextClassLoader().getResource(resourceName);
594                 // found url
595
if (mappingURL != null) {
596                     xmlMapping = createXMLMapping(nameSpace, mappingURL);
597                 }
598             } else {
599                 logger.warn("Namespace found ''{0}}''for node ''{1}'' but this is not an HTTP namespace", nameSpace, nodeName);
600             }
601         } else {
602             logger.warn("No namespace found for node ''{0}''.", nodeName);
603         }
604         // even null namespace will have a default xml mapping
605
mappings.put(nameSpace, xmlMapping);
606         return xmlMapping;
607
608     }
609
610     /**
611      * Create the XML mapping object for the given namespace with the given XML mapping file.
612      * @param nameSpace the namespace of the mapping
613      * @param mappingURL the URL to the XML mapping file
614      * @return an instance of XML mapping
615      * @throws XMLConfigurationException if no XML mapping have been created.
616      */

617     private XMLMapping createXMLMapping(final String JavaDoc nameSpace, final URL JavaDoc mappingURL) throws XMLConfigurationException {
618         XMLMappingBuilder xmlMappingBuilder = new XMLMappingBuilder(mappingURL);
619         xmlMappingBuilder.build();
620         return xmlMappingBuilder.getXmlMapping();
621     }
622 }
623
Popular Tags