001: /**
002: * EasyBeans
003: * Copyright (C) 2006-2007 Bull S.A.S.
004: * Contact: easybeans@ow2.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: XMLConfiguration.java 1970 2007-10-16 11:49:25Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.xmlconfig;
025:
026: import java.lang.reflect.InvocationTargetException;
027: import java.lang.reflect.Method;
028: import java.lang.reflect.ParameterizedType;
029: import java.lang.reflect.Type;
030: import java.net.URL;
031: import java.util.ArrayList;
032: import java.util.HashMap;
033: import java.util.List;
034: import java.util.Map;
035:
036: import org.ow2.easybeans.util.xml.DocumentParser;
037: import org.ow2.easybeans.util.xml.DocumentParserException;
038: import org.ow2.easybeans.xmlconfig.mapping.AttributeMapping;
039: import org.ow2.easybeans.xmlconfig.mapping.ClassMapping;
040: import org.ow2.easybeans.xmlconfig.mapping.XMLMapping;
041: import org.ow2.easybeans.xmlconfig.mapping.XMLMappingBuilder;
042: import org.ow2.util.log.Log;
043: import org.ow2.util.log.LogFactory;
044: import org.w3c.dom.Document;
045: import org.w3c.dom.Element;
046: import org.w3c.dom.NamedNodeMap;
047: import org.w3c.dom.Node;
048: import org.w3c.dom.NodeList;
049:
050: /**
051: * XML configuration class that configure a given object with an xml
052: * configuration file. <br>
053: * Additional information will be searched on the namespace with the resource named easybeans-mapping.xml.
054: * @author Florent Benoit
055: */
056: public class XMLConfiguration {
057:
058: /**
059: * Http:// namespace.
060: */
061: private static final String HTTP_NAMESPACE = "http://";
062:
063: /**
064: * By default, no validation.
065: */
066: private static final boolean VALIDATING_OFF = false;
067:
068: /**
069: * Per-Package mapping file name.
070: */
071: private static final String MAPPING_FILENAME = "easybeans-mapping.xml";
072:
073: /**
074: * Logger.
075: */
076: private Log logger = LogFactory.getLog(XMLConfiguration.class);
077:
078: /**
079: * Validating when parsing xml ?
080: */
081: private boolean validating = VALIDATING_OFF;
082:
083: /**
084: * URL of the configuration file.
085: */
086: private URL xmlConfigurationURL;
087:
088: /**
089: * Mapping between namespace and XMLMapping object.
090: */
091: private Map<String, XMLMapping> mappings = null;
092:
093: /**
094: * Link between name and the objects.
095: */
096: private HashMap<String, Object> configuredElements = null;
097:
098: /**
099: * Build an xml configuration with the given xml configuration file.
100: * @param xmlConfigurationURL the given xml configuration file
101: */
102: public XMLConfiguration(final URL xmlConfigurationURL) {
103: this .xmlConfigurationURL = xmlConfigurationURL;
104: this .mappings = new HashMap<String, XMLMapping>();
105: configuredElements = new HashMap<String, Object>();
106: }
107:
108: /**
109: * Configure the given object with the configuration that was analyzed.
110: * @param object the object to configure.
111: * @throws XMLConfigurationException if configuration fails
112: */
113: public void configure(final Object object)
114: throws XMLConfigurationException {
115: Element rootElement = analyzeXmlFile();
116: configure(object, rootElement, null);
117: }
118:
119: /**
120: * Gets the root element of the XML parsed file.
121: * @return the element object
122: * @throws XMLConfigurationException if analyze fails.
123: */
124: private Element analyzeXmlFile() throws XMLConfigurationException {
125: // Parse the XML file and build all configuration process.
126: Document xmlConfigurationDocument = null;
127: try {
128: xmlConfigurationDocument = DocumentParser.getDocument(
129: xmlConfigurationURL, validating, null);
130: } catch (DocumentParserException e) {
131: throw new XMLConfigurationException(
132: "Cannot get a document on the given url '"
133: + xmlConfigurationURL + "'.", e);
134: }
135:
136: // Get the root element
137: Element rootElement = xmlConfigurationDocument
138: .getDocumentElement();
139:
140: // get namespace of the element
141: String nameSpace = rootElement.getNamespaceURI();
142: // and the name
143: // String nodeName = rootElement.getNodeName();
144:
145: // init mapping for the element
146: getXMLMapping(nameSpace);
147:
148: return rootElement;
149:
150: }
151:
152: /**
153: * Is that the given name should be considered as an XML element (and read the value of this element).
154: * @param name the given name
155: * @param classMapping the mapping used to get the info
156: * @return true if the name should be read from the XML element.
157: */
158: public boolean isElementMapping(final String name,
159: final ClassMapping classMapping) {
160: AttributeMapping attributeMapping = classMapping
161: .getAttributeMapping(name);
162: if (attributeMapping != null) {
163: return attributeMapping.isElement();
164: }
165: return false;
166: }
167:
168: /**
169: * Is that the given name should be used with a list.
170: * @param name the given name
171: * @param classMapping the mapping used to get the info
172: * @return true if the name should be used as a list.
173: */
174: public boolean isListElement(final String name,
175: final ClassMapping classMapping) {
176: AttributeMapping attributeMapping = classMapping
177: .getAttributeMapping(name);
178: if (attributeMapping != null) {
179: return attributeMapping.isListElement();
180: }
181: return false;
182: }
183:
184: /**
185: * Configure the given object by looking at the element (of the
186: * configuration).
187: * @param object the given object to configure.
188: * @param element the element that contains configuration.
189: * @param parentClassMapping the parent class mapping
190: * @throws XMLConfigurationException if configuration fails
191: */
192: public void configure(final Object object, final Element element,
193: final ClassMapping parentClassMapping)
194: throws XMLConfigurationException {
195:
196: // Get all children of the given element
197: NodeList nodeList = element.getChildNodes();
198:
199: // For each element, create object and configure it
200: int length = nodeList.getLength();
201: for (int i = 0; i < length; i++) {
202: Node node = nodeList.item(i);
203:
204: // Get an element, create an instance of the element
205: if (Node.ELEMENT_NODE == node.getNodeType()) {
206:
207: // Is it an element that need to be considered as a string value
208: if (parentClassMapping != null
209: && isElementMapping(node.getNodeName(),
210: parentClassMapping)) {
211: // Get value
212: Node txtNode = node.getFirstChild();
213: String val = null;
214: if (txtNode != null) {
215: val = txtNode.getNodeValue();
216: }
217:
218: // Do nothing if value is null
219: if (val == null) {
220: continue;
221: }
222:
223: // Call setter directly
224: if (!isListElement(node.getNodeName(),
225: parentClassMapping)) {
226: setAttribute(object, node.getNodeName(), val,
227: parentClassMapping);
228: } else {
229: // Need to call the getters and setters
230: addSetElement(object, val, node.getNodeName(),
231: parentClassMapping);
232: }
233: continue;
234: }
235: ClassMapping classMapping = getClassMapping(node
236: .getNamespaceURI(), node.getNodeName());
237:
238: // Build an object for the given class mapping
239: Object newObject = newInstance(node, classMapping);
240:
241: // Try to see if there is a value for the current object
242: Method getterMethod = findSingleGetterMethod(object,
243: newObject);
244: if (getterMethod != null) {
245: // Call the getter method
246: logger.debug("Calling method {0} on object {1}",
247: getterMethod, object);
248: Object getterObject = null;
249: try {
250: getterObject = getterMethod.invoke(object);
251: } catch (IllegalArgumentException e) {
252: throw new XMLConfigurationException(
253: "Cannot call method '" + getterMethod
254: + "' on object '" + object
255: + "'.", e);
256: } catch (IllegalAccessException e) {
257: throw new XMLConfigurationException(
258: "Cannot call method '" + getterMethod
259: + "' on object '" + object
260: + "'.", e);
261: } catch (InvocationTargetException e) {
262: throw new XMLConfigurationException(
263: "Cannot call method '" + getterMethod
264: + "' on object '" + object
265: + "'.", e);
266: }
267:
268: // find an object ?
269: if (getterObject != null) {
270: // yes, then use the existing object
271: newObject = getterObject;
272: }
273: }
274:
275: // Set attributes
276: setAttributes((Element) node, newObject, classMapping);
277:
278: // add or set element on the given object
279: addSetElement(object, newObject, node.getNodeName(),
280: classMapping);
281:
282: // Store element
283: configuredElements.put(node.getNodeName(), newObject);
284:
285: // configure sub element of the new object.
286: configure(newObject, (Element) node, classMapping);
287: }
288: }
289: }
290:
291: /**
292: * Sets the attributes on the given object.
293: * @param node the XML element object
294: * @param object the object to configure
295: * @param classMapping the mapping data
296: * @throws XMLConfigurationException if the attributes cannot be set
297: */
298: protected void setAttributes(final Element node,
299: final Object object, final ClassMapping classMapping)
300: throws XMLConfigurationException {
301: // Set attributes
302: NamedNodeMap attributes = node.getAttributes();
303: int attributesLength = attributes.getLength();
304: for (int a = 0; a < attributesLength; a++) {
305: Node nodeAttribute = attributes.item(a);
306: if (nodeAttribute.getNamespaceURI() == null) {
307: String propertyName = nodeAttribute.getNodeName();
308: String propertyValue = nodeAttribute.getNodeValue();
309: setAttribute(object, propertyName, propertyValue,
310: classMapping);
311: }
312: }
313:
314: // this element is configured to have a value mapped to an attribute ?
315: String elementAttribute = classMapping.getElementAttribute();
316: if (elementAttribute != null) {
317: // Get value
318: Node txtNode = node.getFirstChild();
319: String val = null;
320: if (txtNode != null) {
321: val = txtNode.getNodeValue();
322: }
323: setAttribute(object, elementAttribute, val, classMapping);
324: }
325: }
326:
327: /**
328: * Gets the Mapping for the given nameSpaceURI and the given classname.
329: * @param nameSpaceURI the namespace of the xml element
330: * @param name the name of the class
331: * @return the class mapping object
332: * @throws XMLConfigurationException if no configuration is found
333: */
334: private ClassMapping getClassMapping(final String nameSpaceURI,
335: final String name) throws XMLConfigurationException {
336: // Get classname with the namespace and alias
337: XMLMapping xmlMapping = mappings.get(nameSpaceURI);
338: if (xmlMapping == null) {
339: // Not yet encountered namespace. Try to get a mapping for it.
340: xmlMapping = getXMLMapping(nameSpaceURI);
341: if (xmlMapping == null) {
342: // Still not found.
343: throw new XMLConfigurationException(
344: "No mapping found for namespace '"
345: + nameSpaceURI + "'.");
346: }
347: }
348:
349: // alias present ?
350: ClassMapping classMapping = xmlMapping.getClassMapping(name);
351:
352: // no mapping
353: if (classMapping == null) {
354: throw new XMLConfigurationException(
355: "No class found for element '" + name
356: + "' withing xmlmapping '" + xmlMapping
357: + "'.");
358: }
359: return classMapping;
360: }
361:
362: /**
363: * Build class instance for the given node.
364: * @param node the element of the xml
365: * @param classMapping the mapping between node name and class name
366: * @return the instance of the object
367: * @throws XMLConfigurationException if no new instance can be done.
368: */
369: private Object newInstance(final Node node,
370: final ClassMapping classMapping)
371: throws XMLConfigurationException {
372: // get classname
373: String className = classMapping.getName();
374:
375: // load class
376: Class<?> clazz;
377: try {
378: clazz = Thread.currentThread().getContextClassLoader()
379: .loadClass(className);
380: } catch (ClassNotFoundException e) {
381: throw new XMLConfigurationException(
382: "Cannot load the class '" + className + "'.", e);
383: }
384: logger.debug("Building instance of the class {0}", className);
385: // build a new instance
386: try {
387: return clazz.newInstance();
388: } catch (InstantiationException e) {
389: throw new XMLConfigurationException(
390: "Cannot build an instance of class '" + className
391: + "'.", e);
392: } catch (IllegalAccessException e) {
393: throw new XMLConfigurationException(
394: "Cannot build an instance of class '" + className
395: + "'.", e);
396: } catch (NoClassDefFoundError e) {
397: throw new XMLConfigurationException(
398: "Cannot build an instance of class '" + className
399: + "'.", e);
400: }
401: }
402:
403: /**
404: * Set attribute on the given object.
405: * @param configuringObject the object to configure
406: * @param propertyName the name of the property (for the setter)
407: * @param propertyValue the value of the property
408: * @param classMapping the mapping (if there are attributes aliasing)
409: * @throws XMLConfigurationException if attribute cannot be set.
410: */
411: private void setAttribute(final Object configuringObject,
412: final String propertyName, final String propertyValue,
413: final ClassMapping classMapping)
414: throws XMLConfigurationException {
415:
416: // get class of the configuringObject
417: Class<?> configuringClass = configuringObject.getClass();
418:
419: // Get methods
420: Method[] methods = configuringClass.getMethods();
421:
422: String updatedPropertyName = propertyName;
423:
424: // alias ?
425: AttributeMapping attributeMapping = classMapping
426: .getAttributeMapping(propertyName);
427: if (attributeMapping != null) {
428: String name = attributeMapping.getName();
429: if (name != null) {
430: updatedPropertyName = name;
431: }
432: }
433:
434: String setterName = "set"
435: + updatedPropertyName.substring(0, 1).toUpperCase()
436: + updatedPropertyName.substring(1);
437:
438: Object paramObject = null;
439: // Search setter
440: Method foundMethod = null;
441: if (methods != null) {
442: for (Method m : methods) {
443: // set or add ?
444: if (m.getName().startsWith(setterName)) {
445: // only one arg ?
446: Class<?>[] parameters = m.getParameterTypes();
447: if (parameters.length == 1) {
448: foundMethod = m;
449: Class<?> parameterClass = parameters[0];
450:
451: // Special case of reference
452: if (propertyValue.startsWith("#")
453: && propertyValue.length() >= 2) {
454: paramObject = configuredElements
455: .get(propertyValue.substring(1));
456: } else if (String.class.equals(parameterClass)) {
457: paramObject = propertyValue;
458: } else if (Integer.TYPE.equals(parameterClass)) {
459: paramObject = new Integer(propertyValue);
460: } else if (Boolean.TYPE.equals(parameterClass)) {
461: paramObject = Boolean
462: .valueOf(propertyValue);
463: }
464: break;
465: }
466: }
467: }
468: }
469:
470: // build
471: if (foundMethod != null && paramObject != null) {
472: logger.debug("Calling {0} on object {1} with value {2}",
473: setterName, configuringObject, paramObject);
474: try {
475: foundMethod.invoke(configuringObject, paramObject);
476: } catch (IllegalArgumentException e) {
477: throw new XMLConfigurationException(
478: "Cannot call method '" + foundMethod
479: + "' on object '" + configuringObject
480: + "' with parameter '" + paramObject
481: + "'.", e);
482: } catch (IllegalAccessException e) {
483: throw new XMLConfigurationException(
484: "Cannot call method '" + foundMethod
485: + "' on object '" + configuringObject
486: + "' with parameter '" + paramObject
487: + "'.", e);
488: } catch (InvocationTargetException e) {
489: throw new XMLConfigurationException(
490: "Cannot call method '" + foundMethod
491: + "' on object '" + configuringObject
492: + "' with parameter '" + paramObject
493: + "'.", e);
494: }
495: } else {
496: throw new XMLConfigurationException(
497: "No setter method found for parameter '"
498: + propertyName + "' on class '"
499: + configuringClass + "'.");
500: }
501: }
502:
503: /**
504: * Finds the add(Class c) or set(Class c) methods.<br>
505: * The parameter needs to be a single entry and not a list.
506: * @param configuringObject the object on which call this setter method.
507: * @param paramObject the parameter object
508: * @return the found method if any.
509: */
510: private Method findSingleSetterMethod(
511: final Object configuringObject, final Object paramObject) {
512: Method setterMethod = null;
513:
514: // get class of the configuringObject
515: Class<?> configuringClass = configuringObject.getClass();
516:
517: // Get methods of the class
518: Method[] methods = configuringClass.getMethods();
519:
520: // class of the new object
521: Class<?> parameterClass = paramObject.getClass();
522: String className = parameterClass.getSimpleName();
523:
524: // Search setter
525: if (methods != null) {
526: for (Method m : methods) {
527: // set or add ?
528: if (m.getName().startsWith("set" + className)
529: || m.getName().startsWith("add" + className)) {
530: // only one arg ?
531: Class<?>[] parameters = m.getParameterTypes();
532: if (parameters.length == 1) {
533: // valid parameter
534: if (parameters[0].equals(parameterClass)) {
535: setterMethod = m;
536: break;
537: }
538: }
539: }
540: }
541: }
542: return setterMethod;
543: }
544:
545: /**
546: * Finds the get(Class c) method.<br>
547: * The parameter needs to be a single entry and not a list.
548: * @param configuringObject the object on which call this getter method.
549: * @param paramObject the parameter object
550: * @return the found method if any.
551: */
552: private Method findSingleGetterMethod(
553: final Object configuringObject, final Object paramObject) {
554: Method getterMethod = null;
555:
556: // get class of the configuringObject
557: Class<?> configuringClass = configuringObject.getClass();
558:
559: // Get methods of the class
560: Method[] methods = configuringClass.getMethods();
561:
562: // class of the new object
563: Class<?> parameterClass = paramObject.getClass();
564: String className = parameterClass.getSimpleName();
565:
566: // Search setter
567: if (methods != null) {
568: for (Method m : methods) {
569: // Gettermethod ?
570: if (m.getName().startsWith("get" + className)) {
571: // no Arg ?
572: Class<?>[] parameters = m.getParameterTypes();
573: if (parameters.length == 0) {
574: // valid parameter and return type ?
575: Class<?> returnType = m.getReturnType();
576: if (returnType != null
577: && returnType.equals(paramObject
578: .getClass())) {
579: getterMethod = m;
580: break;
581: }
582: }
583: }
584: }
585: }
586: return getterMethod;
587: }
588:
589: /**
590: * Finds the get(List<Class>) and set(List<Class>) methods. The
591: * parameter needs to be a list with generics.
592: * @param configuringObject the object on which call this setter method.
593: * @param paramObject the parameter object
594: * @param getter the name of the getter
595: * @param setter the name of the setter
596: * @return the getter and setter method with List type.
597: */
598: private Method[] findListGetterSetterMethod(
599: final Object configuringObject, final Object paramObject,
600: final String getter, final String setter) {
601:
602: // get class of the configuringObject
603: Class<?> configuringClass = configuringObject.getClass();
604:
605: // Get methods of the class
606: Method[] methods = configuringClass.getMethods();
607:
608: // class of the new object
609: Class<?> parameterClass = paramObject.getClass();
610:
611: // Search with class of object
612: String className = parameterClass.getSimpleName();
613: // Suffix name (many items)
614: // (special case for y)
615: String argName = null;
616: if (className.endsWith("y")) {
617: argName = className.substring(0, className.length() - 1)
618: + "ies";
619: } else if (className.endsWith("ss")) {
620: argName = className + "es";
621: } else {
622: argName = className + "s";
623: }
624:
625: Method[] searchMethods = searchListGetterSetterMethod(methods,
626: argName, parameterClass, getter, setter);
627:
628: return searchMethods;
629: }
630:
631: /**
632: * Search getter and setter method in the given set of methods. The
633: * getter/setter are used for a List parameter/return type.
634: * @param methods the set of methods to search in
635: * @param argName the name of the argument
636: * @param parameterClass the parameter class (for the generics on the List)
637: * @param getter the name of the getter
638: * @param setter the name of the setter
639: * @return an array of methods, array[0] = getter, array[1] = setter.
640: */
641: private Method[] searchListGetterSetterMethod(
642: final Method[] methods, final String argName,
643: final Class<?> parameterClass, final String getter,
644: final String setter) {
645: Method[] returnedMethods = null;
646:
647: Method getterMethod = null;
648: Method setterMethod = null;
649: // Search getter and setter
650: if (methods != null) {
651: for (Method m : methods) {
652: // setter
653: if (m.getName().startsWith("set" + argName)
654: || m.getName().equals(setter)) {
655: // only one arg ?
656: Class<?>[] parameters = m.getParameterTypes();
657:
658: // setter
659: if (parameters.length == 1) {
660: // ensure it is a List type
661: if (parameters[0].equals(List.class)) {
662: Type[] types = m.getGenericParameterTypes();
663: // look only the first type if any
664: if (types.length == 1) {
665: if (types[0] instanceof ParameterizedType) {
666: ParameterizedType parameterizedType = (ParameterizedType) types[0];
667: Type[] typeArguments = parameterizedType
668: .getActualTypeArguments();
669: if (typeArguments.length == 1) {
670: if (typeArguments[0]
671: .equals(parameterClass)) {
672: // found it !
673: setterMethod = m;
674: }
675: }
676:
677: }
678: }
679: }
680: }
681: } else if (m.getName().startsWith("get" + argName)
682: || m.getName().equals(getter)) {
683: Class<?> returnTypeClass = m.getReturnType();
684: if (returnTypeClass != null
685: && returnTypeClass.equals(List.class)) {
686:
687: // no parameter
688: Class<?>[] parameters = m.getParameterTypes();
689: if (parameters.length == 0) {
690: Type type = m.getGenericReturnType();
691: if (type instanceof ParameterizedType) {
692: ParameterizedType parameterizedType = (ParameterizedType) type;
693: Type[] typeArguments = parameterizedType
694: .getActualTypeArguments();
695: if (typeArguments.length == 1) {
696: if (typeArguments[0]
697: .equals(parameterClass)) {
698: // found it !
699: getterMethod = m;
700: }
701: }
702: }
703: }
704: }
705: }
706: }
707: }
708: // try with interfaces of the current class
709: Class<?>[] interfaces = parameterClass.getInterfaces();
710: if (interfaces != null) {
711: for (Class<?> itf : interfaces) {
712: String itfName = itf.getSimpleName();
713: String argItfName = itfName + "s";
714: returnedMethods = searchListGetterSetterMethod(methods,
715: argItfName, itf, getter, setter);
716: if (returnedMethods != null) {
717: break;
718: }
719: }
720: }
721:
722: if (getterMethod != null && setterMethod != null) {
723: returnedMethods = new Method[2];
724: returnedMethods[0] = getterMethod;
725: returnedMethods[1] = setterMethod;
726: }
727: return returnedMethods;
728: }
729:
730: /**
731: * Add or set element on the given object.
732: * @param configuringObject the object to configure
733: * @param newObject the object to add/set on the configuringObject object
734: * @param attributeName the name of the attribute for this object
735: * @param classMapping the data about all attributes
736: * @throws XMLConfigurationException if newObject cannot be set.
737: */
738: @SuppressWarnings("unchecked")
739: private void addSetElement(final Object configuringObject,
740: final Object newObject, final String attributeName,
741: final ClassMapping classMapping)
742: throws XMLConfigurationException {
743:
744: // Getter and setter set ?
745: String getter = null;
746: String setter = null;
747: boolean isListAttribute = false;
748:
749: // Read properties if any
750: AttributeMapping attributeMapping = classMapping
751: .getAttributeMapping(attributeName);
752: if (attributeMapping != null) {
753: getter = attributeMapping.getGetter();
754: setter = attributeMapping.getSetter();
755: isListAttribute = attributeMapping.isListElement();
756: }
757:
758: // Single setter method object
759: Method foundMethod = null;
760:
761: // Search a single setter if not a list
762: if (!isListAttribute) {
763: foundMethod = findSingleSetterMethod(configuringObject,
764: newObject);
765: }
766:
767: // single setter ?
768: if (foundMethod != null) {
769: logger.debug(
770: "Calling method {0} on object {1} with value {2}",
771: foundMethod, configuringObject, newObject);
772: try {
773: foundMethod.invoke(configuringObject, newObject);
774: } catch (IllegalArgumentException e) {
775: throw new XMLConfigurationException(
776: "Unable to invoke the method '" + foundMethod
777: + "' on '" + configuringObject
778: + "' with arg '" + newObject + "'.", e);
779: } catch (IllegalAccessException e) {
780: throw new XMLConfigurationException(
781: "Unable to invoke the method '" + foundMethod
782: + "' on '" + configuringObject
783: + "' with arg '" + newObject + "'.", e);
784: } catch (InvocationTargetException e) {
785: throw new XMLConfigurationException(
786: "Unable to invoke the method '" + foundMethod
787: + "' on '" + configuringObject
788: + "' with arg '" + newObject + "'.", e);
789: }
790: } else {
791: // try a list getter/setter
792: Method[] getterAndSetter = findListGetterSetterMethod(
793: configuringObject, newObject, getter, setter);
794: if (getterAndSetter != null) {
795: Method getterMethod = getterAndSetter[0];
796: Method setterMethod = getterAndSetter[1];
797:
798: // Get current list
799: List currentList = null;
800: try {
801: logger.debug("Calling method {0} on object {1}",
802: getterMethod, configuringObject);
803: currentList = (List) getterMethod
804: .invoke(configuringObject);
805: } catch (IllegalArgumentException e) {
806: throw new XMLConfigurationException(
807: "Unable to invoke the method '"
808: + getterMethod + "' on '"
809: + configuringObject + "'.", e);
810: } catch (IllegalAccessException e) {
811: throw new XMLConfigurationException(
812: "Unable to invoke the method '"
813: + getterMethod + "' on '"
814: + configuringObject + "'.", e);
815: } catch (InvocationTargetException e) {
816: throw new XMLConfigurationException(
817: "Unable to invoke the method '"
818: + getterMethod + "' on '"
819: + configuringObject + "'.", e);
820: }
821:
822: // Null list, create one
823: if (currentList == null) {
824: currentList = new ArrayList();
825: }
826:
827: // add element
828: logger.debug("Adding element {0} to the list {1}",
829: newObject, currentList);
830: currentList.add(newObject);
831:
832: // set list
833: try {
834: logger
835: .debug(
836: "Calling method {0} on object {1} with value {2}",
837: setterMethod, configuringObject,
838: currentList);
839: setterMethod.invoke(configuringObject, currentList);
840: } catch (IllegalArgumentException e) {
841: throw new XMLConfigurationException(
842: "Unable to invoke the method '"
843: + setterMethod + "' on '"
844: + configuringObject + "'.", e);
845: } catch (IllegalAccessException e) {
846: throw new XMLConfigurationException(
847: "Unable to invoke the method '"
848: + setterMethod + "' on '"
849: + configuringObject + "'.", e);
850: } catch (InvocationTargetException e) {
851: throw new XMLConfigurationException(
852: "Unable to invoke the method '"
853: + setterMethod + "' on '"
854: + configuringObject + "'.", e);
855: }
856:
857: } else {
858: throw new XMLConfigurationException(
859: "No setter method found for parameter '"
860: + newObject.getClass() + "' on class '"
861: + configuringObject.getClass() + "'.");
862: }
863: }
864: }
865:
866: /**
867: * Gets the XML mapping for the given namespace and the node name.
868: * @param nameSpace current XML namespace.
869: * @return the XML mapping object.
870: * @throws XMLConfigurationException if xml mapping is not found.
871: */
872: private XMLMapping getXMLMapping(final String nameSpace)
873: throws XMLConfigurationException {
874: if (nameSpace == null || "".equals(nameSpace)) {
875: throw new XMLConfigurationException(
876: "Empty default namespace not supported.");
877: }
878:
879: // already one ?
880: if (mappings.containsKey(nameSpace)) {
881: return mappings.get(nameSpace);
882: }
883:
884: XMLMapping xmlMapping = null;
885: // With the namespace, get the package name for searching the
886: // <nodeName>-mapping.xml file
887: if (nameSpace != null) {
888: if (nameSpace.startsWith(HTTP_NAMESPACE)) {
889: // get package
890: String packageName = nameSpace.substring(HTTP_NAMESPACE
891: .length());
892: // Compute node name and suffix
893: String resourceName = packageName
894: .replaceAll("\\.", "/")
895: + "/" + MAPPING_FILENAME;
896:
897: URL mappingURL = Thread.currentThread()
898: .getContextClassLoader().getResource(
899: resourceName);
900: // found url
901: if (mappingURL != null) {
902: xmlMapping = createXMLMapping(nameSpace, mappingURL);
903: }
904: } else {
905: logger
906: .warn(
907: "Namespace found ''{0}}'' but this is not an HTTP namespace",
908: nameSpace);
909: }
910: }
911: // even null namespace will have a default xml mapping
912: mappings.put(nameSpace, xmlMapping);
913: return xmlMapping;
914:
915: }
916:
917: /**
918: * Create the XML mapping object for the given namespace with the given XML
919: * mapping file.
920: * @param nameSpace the namespace of the mapping
921: * @param mappingURL the URL to the XML mapping file
922: * @return an instance of XML mapping
923: * @throws XMLConfigurationException if no XML mapping have been created.
924: */
925: private XMLMapping createXMLMapping(final String nameSpace,
926: final URL mappingURL) throws XMLConfigurationException {
927: XMLMappingBuilder xmlMappingBuilder = new XMLMappingBuilder(
928: mappingURL);
929: xmlMappingBuilder.build();
930: return xmlMappingBuilder.getXmlMapping();
931: }
932: }
|