001    /*
002    @license.text@
003     */
004    package biz.hammurapi.config;
005    
006    import java.io.File;
007    import java.io.IOException;
008    import java.io.InputStream;
009    import java.io.Reader;
010    import java.lang.reflect.Array;
011    import java.lang.reflect.Constructor;
012    import java.lang.reflect.Field;
013    import java.lang.reflect.Method;
014    import java.lang.reflect.Modifier;
015    import java.net.MalformedURLException;
016    import java.net.URL;
017    import java.util.ArrayList;
018    import java.util.Collection;
019    import java.util.Collections;
020    import java.util.Enumeration;
021    import java.util.HashMap;
022    import java.util.HashSet;
023    import java.util.Iterator;
024    import java.util.LinkedList;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Set;
028    
029    import javax.xml.parsers.DocumentBuilderFactory;
030    
031    import org.w3c.dom.Element;
032    import org.w3c.dom.Node;
033    import org.w3c.dom.NodeList;
034    import org.xml.sax.InputSource;
035    
036    import biz.hammurapi.config.adapters.File2InputStreamConfigurableAdapter;
037    import biz.hammurapi.config.adapters.InputStream2DomConfigurableAdapter;
038    import biz.hammurapi.config.adapters.URL2InputStreamConfigurableAdapter;
039    import biz.hammurapi.convert.ConvertingService;
040    import biz.hammurapi.convert.DuckConverterFactory;
041    import biz.hammurapi.xml.dom.DOMUtils;
042    
043    
044    /**
045     * Creates and configures objects from DOM {@link org.w3c.dom.Element} (XML file).
046     * DOM Element can be read from InputStream, File or URL.
047     * Instantiation and configuration happens as follows:<P/>
048     * <B>Instantiation</B>
049     * <ul>
050     *      <li>If there is no <code>'type'</code> attribute then type defaults to 
051     * {@link java.lang.String} and text of the element will be returned. 
052     * E.g. &lt;name&gt;Pavel&lt;/name&gt; will yield string 'Pavel'. <code>'type'</code>
053     * attribute name can be changed through {@link biz.hammurapi.config.DomConfigInfo#setCodeExpression(String)}
054     * method. Create {@link biz.hammurapi.config.DomConfigInfo}, change code expression and then
055     * use {@link #DomConfigFactory(DomConfigInfo)} to instantiate DomConfigFactory.
056     * </li>
057     * 
058     * <li>Otherwise class specified in <code>'type'</code> attribute will be loaded and
059     * verified by classAcceptor (if any)</li>
060     * 
061     * <li>If there is no nested <code>'constructor'</code>' element and element text is blank then default
062     * constructor will be used</li>
063     * 
064     * <li>If there is no nested <code>'constructor'</code>' element and element text is not blank then constructor which takes a single argument of type
065     * java.lang.String will be used</li>  
066     * 
067     * <li>If there is nested <code>'constructor'</code> element then <code>'arg'</code> elements of
068     * <code>'constructor'</code> element are iterated to create a list of arguments.
069     * Arguments are constructed in the same way as described here. <code>'arg'</code> element
070     * also supports <code>'context-ref'</code> attribute. If this attribute is set argument
071     * value will be taken from context entry set by {@link DomConfigFactory#setContextEntry(String, Object)} method
072     * </li>
073     * </ul>
074     * <P/>
075     * 
076     * <B>Configuration</B>
077     * <ul>
078     * <li>If element has attribute <code>'url'</code> and instantiated object (instance) 
079     * is instance of {@link biz.hammurapi.config.URLConfigurable} then {@link biz.hammurapi.config.URLConfigurable#configure(URL, Map)}
080     * is invoked to configure instance</li>
081     * 
082     * <li>If element has attribute <code>'file'</code> and instance 
083     * is instance of {@link biz.hammurapi.config.FileConfigurable} then {@link biz.hammurapi.config.FileConfigurable#configure(File, Map)}
084     * is invoked to configure instance</li>
085     * 
086     * <li>If instance is instance of 
087     * {@link biz.hammurapi.config.InputStreamConfigurable} 
088     * then <ul>
089     *      <li>If element has attribute <code>'url'</code> then that url is opened as InsputStream</li>
090     *      <li>If element has attribute <code>'file'</code> then that file is opened as InputStream</li>
091     *      <li>If element has attribute <code>'resource'</code> then that resource is opened as InputStream. 
092     * Instance's class is used to obtain resource which allows to use relative resource names.</li>
093     * </ul> 
094     * then that InputStream is passed to 
095     * {@link biz.hammurapi.config.InputStreamConfigurable#configure(InputStream, Map)}
096     * to configure instance. If none of aforementioned attributes is present then ConfigurationException is thrown.</li>
097     * 
098     * <li>If instance is instance of {@link biz.hammurapi.config.DomConfigurable} then
099     * <ul>
100     *      <li>If element has attribute <code>'url'</code> then that url is opened as InsputStream and parsed to DOM tree</li>
101     *      <li>If element has attribute <code>'file'</code> then that file is opened as InputStream and parsed to DOM tree</li>
102     *      <li>If element has attribute <code>'resource'</code> then that resource is opened as InputStream and parsed to DOM tree. 
103     *      Instance's class is used to obtain resource which allows to use relative resource names.</li>
104     * </ul>
105     * then that parsed document is passed to {@link biz.hammurapi.config.DomConfigurable#configure(Node, Context, ClassLoader)}.
106     * If none of the aforementioned attributes is present then element itself is passed to
107     * {@link biz.hammurapi.config.DomConfigurable#configure(Node, Context, ClassLoader)}</li>
108     * 
109     * <li>If instance is instance of {@link biz.hammurapi.config.Parameterizable} then
110     *      <ul>
111     *      <li>If there are subelements <code>'parameter'</code> with attribute <code>'name'</code>
112     *      then value of <code>'name'</code> is used as parameter name</li>
113     *      <li>Otherwise names of nested elements used as parameter names</li>
114     *      </ul>
115     *  Parameter values are evaluated in the same way as <code>'arg'</code> elements for
116     * constructors.
117     *      {@link biz.hammurapi.config.Parameterizable#setParameter(String, Object)} is invoked for each of parameter elements.
118     * 
119     *  {@link biz.hammurapi.config.Parameterizable#setParameter(String, Object)} is also invoked for context entries 
120     * with names which did not match with parameter names. E.g. if there are two context entries 'age' and 'name' and parameter
121     * 'name' then setParameter("name", <I>value of parameter 'name'</I>) will be invoked and after that 
122     * setParameter("age", <I>value of context entry 'age'</I>) will be invoked.
123     * </li>
124     * 
125     * <li>If instance is instance of {@link biz.hammurapi.config.StringConfigurable} then element text is passed to
126     * {@link StringConfigurable#configure(String, Map)} method</li>
127     * 
128     * <li>If instance is instance of {@link java.util.Map} then <code>'entry'</code> subelements are iterated; <code>'key'</code>
129     * (Configurable through {@link biz.hammurapi.config.DomConfigInfo}) and <code>'value'</code>
130     * (Configurable through {@link biz.hammurapi.config.DomConfigInfo}) subelements are evaluated in the same way as 
131     * <code>'arg'</code> constructors subelements and put to instance by {@link java.util.Map#put(java.lang.Object, java.lang.Object)}</li>
132     * 
133     * <li>If instance is instance of {@link java.util.Collection} then <code>'element'</code> subelements are iterated, elements
134     * are istantiated in the same way as constructor arguments and then placed into instance by invoking {@link java.util.Collection#add(java.lang.Object)}
135     * method.</li>
136     * 
137     * <li>If none of above conditions are met then reflection is used to inject values into instance fields/properties in a similar way as parameters for 
138     * {@link biz.hammurapi.config.Parameterizable} are set. Special note about injection: If field type or setter parameter type (target type) is compatible with 
139     * instantiated value then the value is used as is. Otherwise if target type is compatible with source XML Element then the element is used. If value is instance of 
140     * {@link biz.hammurapi.config.Wrapper} and wrapper's master is compatible with the target type then the master is used. If wrapper is also a component then its setOwner() and start() methods
141     * are invoked before obtaining master. If none of aforementioned conditions are true then value is converted to target type.
142     * using {@link biz.hammurapi.convert.CompositeConverter}.</li> 
143     * 
144     * <li>If object acceptor is not null then its {@link biz.hammurapi.config.ObjectAcceptor#accept(Object)} is invoked
145     * to validate that object has been constructed and configured correctly</li>
146     * 
147     * <li>If instance is instance of {@link biz.hammurapi.config.Validatable} then {@link biz.hammurapi.config.Validatable#validate()} is
148     * invoked for the instance to validate itself. 
149     * </ul>
150     * 
151     * <B>Examples</B>
152     * <OL>
153     * <li><CODE>&lt;name&gt;Pavel&lt;/name&gt;</CODE> will yield java.lang.String with value 'Pavel'</li>
154     * <li><CODE>&lt;age type="java.lang.Integer"&gt;33&lt;/age&gt;</CODE> will yield java.lang.Integer with value '33'</li>
155     * <li><CODE>&lt;config type="org.myself.myproject.MyConfig" url="http://myproject.myself.org/MyConfig.xml"/&gt;</CODE> will load 
156     * configuration from URL and configure MyConfig object</li>
157     * <li><PRE>&lt;config type="org.myself.myproject.MyParameterizableConfig"&gt;
158     *     &lt;parameter name="pi" type="java.lang.Double"&gt;3.14159&lt;/parameter&gt;
159     * &lt;/config&gt;</PRE> will create MyParameterizableConfig object and then invoke its setParameter() method if MyParameterizableConfig 
160     * implements {@link biz.hammurapi.config.Parameterizable} or invoke setPi() method if there is such method. In lenient mode
161     * nothing will happen if there is no setPi() method. Otherwise exception will be thrown.</li>
162     * <li><PRE>&lt;config type="org.myself.myproject.MyParameterizableConfig"&gt;
163     *     &lt;pi type="java.lang.Double"&gt;3.14159&lt;/pi&gt;
164     * &lt;/config&gt;</PRE> same as above.</li>
165     * </OL>
166     * 
167     * It is recommended to use XML Beans generated classes instead of this factory.
168     * 
169     * @author Pavel Vlasov
170     * @version $Revision: 1.12 $
171     */
172    public class DomConfigFactory {
173            public static final String XML_EXTENSION = ".xml";
174            public static final String CONFIG_RESOURCE_PREFIX = "META-INF/config/";
175            
176            public static final String RESOURCE_PREFIX = "resource:";
177            public static final String CLASS_LOADER = ClassLoader.class.getName();
178            public static final String CODE_EXPRESSION = "@type";
179            public static final String MAP_KEY_EXPRESSION = "key";
180            public static final String MAP_VALUE_EXPRESSION = "value";      
181            
182            private static final String CONTEXT_REF = "context-ref";
183            public static final Map PRIMITIVES;
184            private Context context;
185            
186            static {
187                    Map primitives=new HashMap();
188                    primitives.put("boolean", Boolean.TYPE);
189                    primitives.put("byte", Byte.TYPE);
190                    primitives.put("char", Character.TYPE);
191                    primitives.put("double", Double.TYPE);
192                    primitives.put("float", Float.TYPE);
193                    primitives.put("int", Integer.TYPE);
194                    primitives.put("long", Long.TYPE);
195                    primitives.put("short", Short.TYPE);            
196                    PRIMITIVES=Collections.unmodifiableMap(primitives);
197            }
198            
199            private ClassLoader classLoader;
200    
201            /**
202             * Default constructor
203             */
204            public DomConfigFactory() {
205                    super();
206            }
207    
208            /**
209             * Default constructor
210             */
211            public DomConfigFactory(Context context) {
212                    super();
213                    this.context=context;
214            }
215    
216            public DomConfigFactory(ClassLoader classLoader) {              
217                    super();
218                    this.classLoader=classLoader;
219            }
220            
221            public DomConfigFactory(ClassLoader classLoader, Context context) {             
222                    super();
223                    this.classLoader=classLoader;
224                    this.context=context;
225            }
226                    
227            /**
228             * Creates object. Same as create(node, null, null)
229             * @param node
230             * @return
231             * @throws ConfigurationException
232             */
233            public Object create(Node node) throws ConfigurationException {
234                    return create(node, null, null);
235            }
236            
237            /**
238             * Parses file and returns object. Same as create(file, xPath, null, null)
239             * @param file XML configuration file
240             * @param xPath XPath expression, can be null
241             * @return configured object
242             */
243            public Object create(File file, String xPath) throws ConfigurationException, IOException {
244                    return create(file, xPath, null, null);
245            }
246            
247            /**
248             * Parses file and returns object
249             * @param file XML configuration file
250             * @param xPath XPath expression, can be null
251             * @param classAcceptor Class acceptor, validates that class about to be instantiated is 'the right one'
252             * @param objectAcceptor Object acceptor, validates instantiated object.
253             * @return Configured object
254             */
255            public Object create(File file, String xPath, ClassAcceptor classAcceptor, ObjectAcceptor objectAcceptor) throws ConfigurationException, IOException {
256                    try {
257                            Node node = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file).getDocumentElement();
258                            if (xPath!=null) {
259                                    node=DOMUtils.selectSingleNode(node, xPath);
260                            }                       
261                            return create(node, classAcceptor, objectAcceptor);
262                    } catch (Exception e) {
263                            throw new ConfigurationException(e);
264                    }
265            }
266            
267            /**
268             * Same as create(in, xPath, null, null)
269             * @param in Input stream
270             * @param xPath XPath expression, can be null
271             * @return Configured object
272             * @throws ConfigurationException
273             * @throws IOException
274             */
275            public Object create(InputStream in, String xPath) throws ConfigurationException, IOException {
276                    return create(in, xPath, null, null);
277            }
278            
279            /**
280             * Creates and configures object from InputStream
281             * @param in Input stream
282             * @param xPath XPath expression, can be null
283             * @param classAcceptor
284             * @param objectAcceptor
285             * @return Configured object
286             * @throws ConfigurationException
287             * @throws IOException
288             */
289            public Object create(InputStream in, String xPath, ClassAcceptor classAcceptor, ObjectAcceptor objectAcceptor) throws ConfigurationException, IOException {
290                    try {
291                            Node node = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in).getDocumentElement();
292                            if (xPath!=null) {
293                                    node=DOMUtils.selectSingleNode(node, xPath);
294                            }
295                            return create(node, classAcceptor, objectAcceptor);
296                    } catch (Exception e) {
297                            throw new ConfigurationException(e);
298                    }               
299            }
300            
301            /**
302             * Same as create(in, xPath, null, null)
303             * @param in Reader
304             * @param xPath XPath expression, can be null
305             * @return Configured object
306             * @throws ConfigurationException
307             * @throws IOException
308             */
309            public Object create(Reader in, String xPath) throws ConfigurationException, IOException {
310                    return create(in, xPath, null, null);
311            }
312            
313            /**
314             * Creates and configures object from InputStream
315             * @param in Reader
316             * @param xPath XPath expression, can be null
317             * @param classAcceptor
318             * @param objectAcceptor
319             * @return Configured object
320             * @throws ConfigurationException
321             * @throws IOException
322             */
323            public Object create(Reader in, String xPath, ClassAcceptor classAcceptor, ObjectAcceptor objectAcceptor) throws ConfigurationException, IOException {
324                    try {
325                            Node node = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(in)).getDocumentElement();
326                            if (xPath!=null) {
327                                    node=DOMUtils.selectSingleNode(node, xPath);
328                            }
329                            return create(node, classAcceptor, objectAcceptor);
330                    } catch (Exception e) {
331                            throw new ConfigurationException(e);
332                    }               
333            }
334            
335            /**
336             * Same as create(url, xPath, null, null)
337             * @param url URL to read configuration from
338             * @param xPath XPath expression, can be null
339             * @return Configured object
340             * @throws ConfigurationException
341             * @throws IOException
342             */
343            public Object create(URL url, String xPath) throws ConfigurationException, IOException {
344                    return create(url, xPath, null, null);
345            }
346            
347            /**
348             * Creates and configures object from URL
349             * @param url Url
350             * @param xPath XPath expression, can be null
351             * @param classAcceptor
352             * @param objectAcceptor
353             * @return Configured object
354             * @throws ConfigurationException
355             * @throws IOException
356             */
357            public Object create(URL url, String xPath, ClassAcceptor classAcceptor, ObjectAcceptor objectAcceptor) throws ConfigurationException, IOException {
358                    try {
359                            Node node = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(url.openStream()).getDocumentElement();
360                            if (xPath!=null) {
361                                    node=DOMUtils.selectSingleNode(node, xPath);
362                            }                       
363                            return create(node, classAcceptor, objectAcceptor);
364                    } catch (Exception e) {
365                            throw new ConfigurationException(e);
366                    }               
367            }
368            
369            /**
370             * Creates and configures object
371             * @param node
372             * @param classAcceptor
373             * @param objectAcceptor
374             * @param cxpa Cached XPath API to accelerate XPath expressions evaluation.
375             * @return Configured object
376             * @throws ConfigurationException
377             */
378            public Object create(Node node, ClassAcceptor classAcceptor, ObjectAcceptor objectAcceptor) throws ConfigurationException {             
379                    try {                   
380                            String className=DOMUtils.eval(node, CODE_EXPRESSION).toString();                       
381                            if (className.trim().length()==0) {
382                                    if (classAcceptor!=null) {
383                                            classAcceptor.accept(String.class);
384                                    }
385                                    return DOMUtils.eval(node, "text()").toString();
386                            } 
387                            
388                            Class clazz = forName(className);
389                            if (classAcceptor!=null) {
390                                    classAcceptor.accept(clazz);
391                            }
392                            
393                            Object instance;
394                            Node constructorNode=DOMUtils.selectSingleNode(node, "constructor");
395                            if (constructorNode==null) {
396                                    String body=DOMUtils.eval(node, "text()").toString().trim();
397                                    if (body.length()==0 || DomConfigurable.class.isAssignableFrom(clazz)) {
398                                            instance=clazz.newInstance();
399                                    } else {
400                                            Constructor c=clazz.getConstructor(new Class[] {String.class});
401                                            instance=c.newInstance(new Object[] {body});
402                                    }
403                            } else {
404                                    NodeList argList=DOMUtils.selectNodeList(constructorNode, "arg");
405                                    Class[] argTypes=new Class[argList.getLength()];
406                                    for (int i=0; i<argList.getLength(); i++) {
407                                            String argTypeName=DOMUtils.eval(argList.item(i), CODE_EXPRESSION).toString();
408                                            if (argTypeName.trim().length()==0) {
409                                                    argTypes[i]=String.class;                                               
410                                            } else {
411                                                    argTypes[i]=(Class) PRIMITIVES.get(argTypeName);
412                                                    if (argTypes[i]==null) {
413                                                            argTypes[i]=forName(argTypeName);
414                                                    }
415                                            }
416                                    }                                                               
417                                    
418                                    Constructor constructor=clazz.getConstructor(argTypes);
419                                    Object[] args=new Object[argList.getLength()];
420                                    for (int i=0; i<argList.getLength(); i++) {
421                                            Element argElement = ((Element) argList.item(i));
422                                            if (argTypes[i].isPrimitive()) {
423                                                    args[i] = ConvertingService.convert(
424                                                                    DOMUtils.eval(argList.item(i), "text()").toString(),
425                                                                    argTypes[i]);                                                   
426                                            } else if (argElement.hasAttribute(CONTEXT_REF)) {
427                                                    args[i]=context.get(argElement.getAttribute(CONTEXT_REF));
428                                            } else {
429                                                    args[i]=create(argList.item(i),null,null);
430                                            }
431                                    }
432                                    instance=constructor.newInstance(args);
433                            }
434                                            
435                            if (hasAttribute(node, "url") && instance instanceof URLConfigurable) {
436                                    ((URLConfigurable) instance).configure(new URL(((Element) node).getAttribute("url")), context, classLoader);
437                            } else if (hasAttribute(node, "file") && instance instanceof FileConfigurable) {
438                                    ((FileConfigurable) instance).configure(new File(((Element) node).getAttribute("file")), context, classLoader);
439                            } else if (instance instanceof InputStreamConfigurable) {
440                                    if (hasAttribute(node, "url")) {
441                                            new URL2InputStreamConfigurableAdapter((InputStreamConfigurable) instance).configure(new URL(((Element) node).getAttribute("url")), context, classLoader);
442                                    } else if (hasAttribute(node, "file")) {
443                                            new File2InputStreamConfigurableAdapter((InputStreamConfigurable) instance).configure(new File(((Element) node).getAttribute("file")), context, classLoader);
444                                    } else if (hasAttribute(node, "resource")) {                                    
445                                            ((InputStreamConfigurable) instance).configure(clazz.getResourceAsStream(((Element) node).getAttribute("resource")), context, classLoader);
446                                    } else {
447                                            throw new ConfigurationException("Cannot configure "+instance.getClass().getName());
448                                    }
449                            } else if (instance instanceof DomConfigurable) { // Dom configurable
450                                    if (hasAttribute(node, "url")) {
451                                            new URL2InputStreamConfigurableAdapter(
452                                                    new InputStream2DomConfigurableAdapter(
453                                                            (DomConfigurable) instance)).configure(new URL(((Element) node).getAttribute("url")), context, classLoader);
454                                    } else if (hasAttribute(node, "file")) {
455                                            new File2InputStreamConfigurableAdapter(
456                                                    new InputStream2DomConfigurableAdapter(
457                                                            (DomConfigurable) instance)).configure(new File(((Element) node).getAttribute("file")), context, classLoader);
458                                    } else if (hasAttribute(node, "resource")) {                                    
459                                                    new InputStream2DomConfigurableAdapter((DomConfigurable) instance).configure(getClass().getResourceAsStream(((Element) node).getAttribute("resource")), context, classLoader);
460                                    } else {
461                                            ((DomConfigurable) instance).configure(node, context, classLoader);
462                                    }
463                            } else if (instance instanceof Parameterizable) { // Parameterizable
464                                    Map localContext=new HashMap();
465                                    
466                                    if (DOMUtils.selectSingleNode(node, "parameter[@name]")==null) { 
467                                            // Use element names as parameter names
468                                            NodeList nl=node.getChildNodes();
469                                            for (int i=0; i<nl.getLength(); i++) {
470                                                    if (nl.item(i) instanceof Element) {
471                                                            String parameterName = nl.item(i).getNodeName();                                                        
472                                                            ((Parameterizable) instance).setParameter(parameterName, getValue((Element) nl.item(i)));
473                                                            localContext.remove(parameterName);
474                                                    }
475                                            }
476                                    } else {
477                                            NodeList nl=DOMUtils.selectNodeList(node, "parameter[@name]");
478                                            for (int i=0, l=nl.getLength(); i<l; ++i) {
479                                                    Element paramElement = (Element) nl.item(i);
480                                                    String parameterName = paramElement.getAttribute("name");
481                                                    ((Parameterizable) instance).setParameter(parameterName, getValue(paramElement));
482                                                    localContext.remove(parameterName);
483                                            } 
484                                    }                               
485                                    
486                                    Iterator it=localContext.entrySet().iterator();
487                                    while (it.hasNext()) {
488                                            Map.Entry entry=(Map.Entry) it.next();
489                                            ((Parameterizable) instance).setParameter((String) entry.getKey(), entry.getValue());
490                                    }
491                            } else if (instance instanceof StringConfigurable) { // String configurable
492                                    ((StringConfigurable) instance).configure(DOMUtils.eval(node, "text()").toString(), context);
493                            } else if (instance instanceof Map) { // Map
494                                    NodeList nl=DOMUtils.selectNodeList(node, "entry");
495                                    for (int i=0, l=nl.getLength(); i<l; ++i) {
496                                            Element entryElement = (Element) nl.item(i);
497                                            ((Map) instance).put(getValue((Element) DOMUtils.selectSingleNode(entryElement,MAP_KEY_EXPRESSION)),
498                                                    getValue((Element) DOMUtils.selectSingleNode(entryElement,MAP_VALUE_EXPRESSION)));
499                                    }                               
500                            } else if (instance instanceof Collection) { // Collection
501                                    NodeList nl=DOMUtils.selectNodeList(node, "element");
502                                    for (int i=0, l=nl.getLength(); i<l; ++i) {
503                                            Element element = (Element) nl.item(i);
504                                            ((Collection) instance).add(getValue(element));
505                                    }                                                               
506                            } else {
507                                    Map toInject=new HashMap();
508                                    Context localContext=new MapContext(toInject, context);
509                                    
510                                    if (DOMUtils.selectSingleNode(node, "parameter[@name]")==null) { 
511                                            // Use element names as parameter names
512                                            NodeList nl=node.getChildNodes();
513                                            for (int i=0; i<nl.getLength(); i++) {
514                                                    if (nl.item(i) instanceof Element) {
515                                                            String name = nl.item(i).getNodeName();                                                 
516                                                            Element element = (Element) nl.item(i);
517                                                            Object existing = toInject.get(name);
518                                                            if (existing==null) {
519                                                                    toInject.put(name, getInjectEntry(element));
520                                                            } else if (existing instanceof InjectEntryCollection) {
521                                                                    ((Collection) existing).add(getInjectEntry(element));
522                                                            } else {
523                                                                    Collection col = new InjectEntryCollection();
524                                                                    col.add(existing);
525                                                                    col.add(getInjectEntry(element));
526                                                                    toInject.put(name, col);
527                                                            }
528                                                    }
529                                            }
530                                    } else {
531                                            NodeList nl =DOMUtils.selectNodeList(node, "parameter[@name]");
532                                            for (int i=0, l=nl.getLength(); i<l; ++i) {
533                                                    Element paramElement = (Element) nl.item(i);
534                                                    String name = paramElement.getAttribute("name");
535                                                    Object existing = toInject.get(name);
536                                                    if (existing==null) {
537                                                            toInject.put(name, getInjectEntry(paramElement));
538                                                    } else if (existing instanceof InjectEntryCollection) {
539                                                            ((Collection) existing).add(getInjectEntry(paramElement));
540                                                    } else {
541                                                            Collection col = new InjectEntryCollection();
542                                                            col.add(existing);
543                                                            col.add(getInjectEntry(paramElement));
544                                                            toInject.put(name, col);
545                                                    }
546                                            } 
547                                    }
548                                    
549                                    inject(instance, localContext);
550                            }
551                            
552                            if (objectAcceptor!=null) {
553                                    objectAcceptor.accept(instance);
554                            }
555                            
556                            if (instance instanceof Validatable) {
557                                    ((Validatable) instance).validate();
558                            }
559                                                    
560                            return instance;
561                    } catch (Exception e) {
562                            throw new ConfigurationException(e);
563                    }
564            }
565            
566            /**
567             * Marker class
568             * @author Tatyana Konukova
569             *
570             */
571            private static class InjectEntryCollection extends ArrayList {
572                    
573            }
574            
575            /**
576             * Holder for element and its instantiation.
577             * @author Pavel Vlasov
578             * @revision $Revision$
579             */
580            private class InjectEntry {
581                    Element element;
582                    Object instance;
583                    
584                    /**
585                     * @param element
586                     * @param instance
587                     */
588                    public InjectEntry(Element element, Object instance) {
589                            super();
590                            this.element = element;
591                            this.instance = instance;
592                    }
593                                    
594            }
595    
596        /**
597             * @param className
598             * @return
599             * @throws ClassNotFoundException
600             */
601            private Class forName(String className) throws ClassNotFoundException {
602                    return classLoader==null ? Class.forName(className) : classLoader.loadClass(className);
603            }
604    
605            /**
606             * Converts instantiated value to InjectEntry
607             * @param cxpa
608             * @param parameterElement
609             * @return
610             * @throws ConfigurationException
611             */
612            private InjectEntry getInjectEntry(Element parameterElement) throws ConfigurationException {
613                    return new InjectEntry(parameterElement, getValue(parameterElement));
614            }
615            
616            /**
617             * @param cxpa
618             * @param parameterElement
619             * @return
620             * @throws ConfigurationException
621             */
622            private Object getValue(Element parameterElement) throws ConfigurationException {
623                    if (parameterElement.hasAttribute(CONTEXT_REF)) {
624                            return context.get((parameterElement).getAttribute(CONTEXT_REF));
625                    }
626                    
627                    return create(parameterElement,null,null);
628            }
629            
630            
631            /**
632             * Converts InjectEntry to target class.
633             * Takes into account wrappers and org.w3c.Element
634             * @param entry
635             * @param targetClass
636             * @return
637             */
638            private static Object getValue(Object value, Class targetClass, Object owner) {
639                    
640                    if (value==null) {
641                            return null;
642                    }
643                    
644                    if (value instanceof InjectEntryCollection) {
645                            return value;
646                    }
647                    
648                    if (value instanceof InjectEntry) {
649                            InjectEntry injectEntry=(InjectEntry) value;
650                            
651                            // If instance is already compatible - return instance.
652                            if (targetClass.isInstance(injectEntry.instance)) {
653                                    return injectEntry.instance;
654                            }
655                            
656                            // If element is compatible - return element
657                            if (targetClass.isInstance(injectEntry.element)) {
658                                    return injectEntry.element;
659                            }
660                            
661                            // If is Wrapper and master is compatible - return master
662                            if (injectEntry.instance instanceof Wrapper) {                  
663                                    //Start wrapper if it is also a component.
664                                    if (injectEntry.instance instanceof Component) {
665                                            try {
666                                                    Component component = (Component) injectEntry.instance;
667                                                    component.setOwner(owner);
668                                                    component.start();
669                                            } catch (ConfigurationException e) {
670                                                    throw new RuntimeConfigurationException("Could not start wrapper component "+injectEntry.instance.getClass().getName()+": "+e, e);
671                                            }
672                                    }
673                                    Object master=((Wrapper) injectEntry.instance).getMaster();
674                                    if (targetClass.isInstance(master)) {
675                                            return master;
676                                    }
677                            }
678            
679                            // The last resort - use converter.
680                            return ConvertingService.convert(injectEntry.instance, targetClass);
681                    }
682                    
683                    // If instance is already compatible - return instance.
684                    if (targetClass.isInstance(value)) {
685                            return value;
686                    }
687                            
688                    // The last resort - use converter.
689                    return ConvertingService.convert(value, targetClass);
690            }
691    
692            /**
693         * Sets property (field or through setter or appender) using reflection.
694         * This method can inject single or multiple values from the context to the instance
695         * using setXXX(argType arg), setXXX(argType[] arg), setXXX(Collection args), or
696         * addXXX(argType arg). For multi-value injections the context shall return collection, 
697         * array, or iterator instance from its get() method.
698             * @param instance
699         * @param context Source of data to inject.
700             */
701            public static void inject(Object instance, Context context) throws ConfigurationException {
702                    Method[] ma=instance.getClass().getMethods();
703                    
704                    Set usedKeys = new HashSet();
705                    LinkedList setters = new LinkedList();
706                    LinkedList appenders = new LinkedList();
707                    for (int i=0; i<ma.length; i++) {
708                            if (Modifier.isPublic(ma[i].getModifiers()) && ma[i].getParameterTypes().length==1) {
709                                    if (ma[i].getName().startsWith("set")) {
710                                            setters.add(ma[i]);
711                                    } else if (ma[i].getName().startsWith("add")) {
712                                            appenders.add(ma[i]);
713                                    }
714                            }
715                    }
716                    
717                    // Injection of single values using setters.
718                    Method[] sa = (Method[]) setters.toArray(new Method[setters.size()]);
719                    for (int i=0; i<sa.length; ++i) {
720                            Method m=sa[i];
721                            if (m!=null) {
722                                    String methodName = m.getName();
723                                    int methodIndex = i;
724                                    int methodNameLength = methodName.length();
725                                    Class pt = m.getParameterTypes()[0];
726                                    String key=methodNameLength==3 ? pt.getName() : (Character.toLowerCase(methodName.charAt(3)) + (methodNameLength<5 ? "" : methodName.substring(4)));
727                                    if (!usedKeys.contains(key)) {
728                                            Object value=getValue(context.get(key), pt, instance);
729                                            
730                                            // Take single value.
731                                            if (value instanceof InjectEntryCollection) {
732                                                    if (!((Collection) value).isEmpty() && !pt.isArray()) {
733                                                            value = getValue(((InjectEntryCollection) value).get(0), pt, instance);
734                                                    } else {
735                                                            value = null;
736                                                    }
737                                            }
738                                                                            
739                                            if (value!=null) {
740                                                    for (int j=0; j<sa.length; ++j) {
741                                                            Method m2=sa[j];
742                                                            if (m2!=null && m2.getName().equals(m.getName()) && pt.isAssignableFrom(m2.getParameterTypes()[0])) {
743                                                                    m=m2;
744                                                                    methodIndex=j;
745                                                            }
746                                                    }
747                                                    
748                                                    try {
749                                                            m.invoke(instance, new Object[] {value});
750                                                    } catch (Exception e) {
751                                                            throw new ConfigurationException(e);
752                                                    }
753                                                    
754                                                    usedKeys.add(key);
755                                                    setters.remove(m);
756                                                    sa[methodIndex]=null;
757                                            }                       
758                                    }
759                            }
760                    }
761                    
762                    // Injection of single values using appenders.
763                    Method[] aa = (Method[]) appenders.toArray(new Method[appenders.size()]);
764                    for (int i=0; i<aa.length; ++i) {
765                            Method m=aa[i];
766                            if (m!=null) {
767                                    String methodName = m.getName();
768                                    int methodIndex = i;
769                                    int methodNameLength = methodName.length();
770                                    Class pt = m.getParameterTypes()[0];
771                                    String key=methodNameLength==3 ? pt.getName() : (Character.toLowerCase(methodName.charAt(3)) + (methodNameLength<5 ? "" : methodName.substring(4)));
772                                    if (!usedKeys.contains(key)) {
773                                            Object cValue = context.get(key);
774                                            Object value=getValue(cValue, pt, instance);
775                                                                                            
776                                            if (value!=null && !(value instanceof InjectEntryCollection)) {
777                                                    for (int j=0; j<aa.length; ++j) {
778                                                            Method m2=aa[j];
779                                                            if (m2!=null && m2.getName().equals(m.getName()) && pt.isAssignableFrom(m2.getParameterTypes()[0])) {
780                                                                    m=m2;
781                                                                    methodIndex=j;
782                                                            }
783                                                    }
784                                                    
785                                                    try {
786                                                            m.invoke(instance, new Object[] {value});
787                                                    } catch (Exception e) {
788                                                            throw new ConfigurationException(e);
789                                                    }
790                                                    
791                                                    usedKeys.add(key);
792                                                    appenders.remove(m);
793                                                    aa[methodIndex] = null;
794                                            }                       
795                                    }
796                            }
797                    }
798                    
799                    // Injection of multiple values using appenders.
800                    Iterator ait = appenders.iterator();
801                    while (ait.hasNext()) {
802                            Method m=(Method) ait.next();
803                            String methodName = m.getName();
804                            int methodNameLength = methodName.length();
805                            Class pt = m.getParameterTypes()[0];
806                            String key=methodNameLength==3 ? pt.getName() : (Character.toLowerCase(methodName.charAt(3)) + (methodNameLength<5 ? "" : methodName.substring(4)));
807                            if (!usedKeys.contains(key)) {
808                                    Object val=context.get(key);
809                                    Iterator vit = null;
810                                    if (val instanceof Iterator) {
811                                            vit = (Iterator) val;
812                                    } else if (val instanceof Collection) {
813                                            vit = ((Collection) val).iterator();
814                                    } else if (val!=null && val.getClass().isArray()) {
815                                            Collection col = new ArrayList();
816                                            for (int i=0, l=Array.getLength(val); i<l; ++i) {
817                                                    col.add(Array.get(val, i));
818                                            }
819                                            vit = col.iterator();
820                                    }
821                                    
822                                    if (vit!=null) {
823                                            usedKeys.add(key);
824                                            while (vit.hasNext()) {
825                                                    Object value = getValue(vit.next(), pt, instance);
826                                                    if (value!=null) {                              
827                                                            try {
828                                                                    m.invoke(instance, new Object[] {value});
829                                                            } catch (Exception e) {
830                                                                    throw new ConfigurationException(e);
831                                                            }
832                                                    }
833                                            }                       
834                                            ait.remove();
835                                    }
836                            }
837                    }
838                                                                    
839                    // Injection of multiple values using setters which take collections.
840                    Iterator sit=setters.iterator();
841                    while (sit.hasNext()) {
842                            Method m=(Method) sit.next();
843                            String methodName = m.getName();
844                            int methodNameLength = methodName.length();
845                            Class pt = m.getParameterTypes()[0];
846                            String key=methodNameLength==3 ? pt.getName() : (Character.toLowerCase(methodName.charAt(3)) + (methodNameLength<5 ? "" : methodName.substring(4)));
847                            if (!usedKeys.contains(key) && pt.isAssignableFrom(ArrayList.class)) {
848                                    List values = new ArrayList();
849                                    
850                                    Object val=context.get(key);
851                                    if (val instanceof Iterator) {
852                                            Iterator vit = (Iterator) val;
853                                            while (vit.hasNext()) {
854                                                    values.add(getValue(vit.next(), Object.class, instance));
855                                            }
856                                            
857                                    } else if (val instanceof Collection) {
858                                            Iterator vit = ((Collection) val).iterator();
859                                            while (vit.hasNext()) {
860                                                    values.add(getValue(vit.next(), Object.class, instance));
861                                            }
862                                    } else if (val!=null && val.getClass().isArray()) {
863                                            for (int i=0, l=Array.getLength(val); i<l; ++i) {
864                                                    values.add(Array.get(val, i));
865                                            }
866                                    } else {
867                                            Object vl = getValue(val, Object.class, instance);
868                                            if (vl!=null) {
869                                                    values.add(vl);
870                                            }
871                                    }
872                                    
873                                    if (!values.isEmpty()) {
874                                            try {
875                                                    m.invoke(instance, new Object[] {values});
876                                                    usedKeys.add(key);
877                                            } catch (Exception e) {
878                                                    throw new ConfigurationException(e);
879                                            }
880                                    }
881                                    sit.remove();
882                            }
883                    }
884                    
885                    // Injection of multiple values using setters which take arrays.
886                    sit=setters.iterator();
887                    while (sit.hasNext()) {
888                            Method m=(Method) sit.next();
889                            String methodName = m.getName();
890                            int methodNameLength = methodName.length();
891                            Class pt = m.getParameterTypes()[0];
892                            String key=methodNameLength==3 ? pt.getName() : (Character.toLowerCase(methodName.charAt(3)) + (methodNameLength<5 ? "" : methodName.substring(4)));
893                            if (!usedKeys.contains(key) && pt.isArray()) {
894                                    List values = new ArrayList();
895                                    
896                                    Object val=context.get(key);
897                                    Class componentType = pt.getComponentType();
898                                    if (val instanceof Iterator) {
899                                            Iterator vit = (Iterator) val;
900                                            while (vit.hasNext()) {
901                                                    values.add(getValue(vit.next(), componentType, instance));
902                                            }
903                                    } else if (val instanceof Collection) {
904                                            Iterator vit = ((Collection) val).iterator();
905                                            while (vit.hasNext()) {
906                                                    values.add(getValue(vit.next(), componentType, instance));
907                                            }
908                                    } else if (val!=null && val.getClass().isArray()) {
909                                            for (int i=0, l=Array.getLength(val); i<l; ++i) {
910                                                    values.add(Array.get(val, i));
911                                            }
912                                    } else {
913                                            Object vl = getValue(val, componentType, instance);
914                                            if (vl!=null) {
915                                                    values.add(vl);
916                                            }
917                                    }
918                                    
919                                    if (!values.isEmpty()) {
920                                            try {
921                                                    Object array = Array.newInstance(componentType, values.size());
922                                                    Iterator vit = values.iterator();
923                                                    for (int i=0; vit.hasNext(); ++i) {
924                                                            Array.set(array, i, vit.next());
925                                                    }
926                                                    m.invoke(instance, new Object[] {array});
927                                                    usedKeys.add(key);
928                                            } catch (Exception e) {
929                                                    throw new ConfigurationException(e);
930                                            }
931                                    }
932                                    sit.remove();
933                            }
934                    }
935                    
936                    // Injection of first of multiple values using setters.
937                    sit = setters.iterator();
938                    while (sit.hasNext()) {
939                            Method m=(Method) sit.next();
940                            String methodName = m.getName();
941                            int methodNameLength = methodName.length();
942                            Class pt = m.getParameterTypes()[0];
943                            String key=methodNameLength==3 ? pt.getName() : (Character.toLowerCase(methodName.charAt(3)) + (methodNameLength<5 ? "" : methodName.substring(4)));
944                            if (!usedKeys.contains(key)) {
945                                    Object val=context.get(key);
946                                    Iterator vit = null;
947                                    if (val instanceof Iterator) {
948                                            vit = (Iterator) val;
949                                    } else if (val instanceof Collection) {
950                                            vit = ((Collection) val).iterator();
951                                    } else if (val!=null && val.getClass().isArray()) {
952                                            Collection col = new ArrayList();
953                                            for (int i=0, l=Array.getLength(val); i<l; ++i) {
954                                                    col.add(Array.get(val, i));
955                                            }
956                                            vit = col.iterator();
957                                    }
958                                    
959                                    if (vit!=null) {
960                                            usedKeys.add(key);
961                                            while (vit.hasNext()) {
962                                                    Object value = getValue(vit.next(), pt, instance);
963                                                    if (value!=null) {                              
964                                                            try {
965                                                                    m.invoke(instance, new Object[] {value});
966                                                            } catch (Exception e) {
967                                                                    throw new ConfigurationException(e);
968                                                            }
969                                                            break;
970                                                    }
971                                            }                       
972                                            sit.remove();
973                                    }
974                            }
975                    }
976                    
977                    
978                    Field[] fa=instance.getClass().getFields();
979                    for (int i=0; i<fa.length; i++) {
980                            if (Modifier.isPublic(fa[i].getModifiers()) && usedKeys.add(fa[i].getName())) {
981                                    try {
982                                            Object value=getValue(context.get(fa[i].getName()), fa[i].getType(), instance);
983                                            if (value!=null) {
984                                                    fa[i].set(instance, value);
985                                            }
986                                    } catch (IllegalArgumentException e) {
987                                            throw new ConfigurationException(e);
988                                    } catch (IllegalAccessException e) {
989                                            throw new ConfigurationException(e);
990                                    }
991                            }
992                    }
993            }
994            
995            /**
996         * @param node
997         * @return
998         */
999        private boolean hasAttribute(Node node, String attribute) {
1000            return node instanceof Element && ((Element) node).hasAttribute(attribute);
1001        }  
1002            
1003            public static void main(final String[] args) {
1004                    final long start=System.currentTimeMillis();
1005                    if (args.length==0) {
1006                            System.err.println("Usage: java <options> "+DomConfigFactory.class.getName()+" <configuration URL> [<additional parameters>]");
1007                            System.exit(1);
1008                    }
1009                    
1010                    final boolean stopInHook = "yes".equalsIgnoreCase(System.getProperty("biz.hammurapi.config.DomConfigFactory:shutdownHook"));
1011                    final Object[] oa = {null};
1012                    
1013                    if (stopInHook) {
1014                            Runtime.getRuntime().addShutdownHook(
1015                                            new Thread() {
1016                                                    public void run() {
1017                                                            if (oa[0] instanceof Component) {
1018                                                                    try {                                                   
1019                                                                            ((Component) oa[0]).stop();
1020                                                                    } catch (ConfigurationException e) {
1021                                                                            System.err.println("Could not properly stop "+oa[0]);
1022                                                                            e.printStackTrace();
1023                                                                    }
1024                                                                    System.out.println("Total execution time: "+((System.currentTimeMillis()-start)/1000)+" sec.");
1025                                                            }
1026                                                    }
1027                                            });                     
1028                    }
1029                                                    
1030                    RestartCommand run = new RestartCommand() {
1031                            
1032                            int attempt;
1033                            
1034                            public void run() {
1035                                    try {
1036                                            if (attempt > 0) {   
1037                                                    long restartDelay = getRestartDelay();
1038                                                    System.out.print("Restarting in "+restartDelay+" milliseconds. Attempt " + (attempt+1));
1039                                                    try {
1040                                                            Thread.sleep(restartDelay);
1041                                                    } catch (InterruptedException ie) {
1042                                                            ie.printStackTrace();
1043                                                            System.exit(4);
1044                                                    }
1045                                            }
1046                                            
1047                                            ++attempt;
1048                                            
1049                                            DomConfigFactory factory=new DomConfigFactory();
1050                                            
1051                                            if (args[0].startsWith("url:")) {
1052                                                    oa[0] = factory.create(new URL(args[0].substring("url:".length())), null);
1053                                            } else if (args[0].startsWith(RESOURCE_PREFIX)) {
1054                                                    InputStream stream = DomConfigFactory.class.getClassLoader().getResourceAsStream(args[0].substring(RESOURCE_PREFIX.length()));
1055                                                    if (stream==null) {
1056                                                            System.err.println("Resource does not exist.");
1057                                                            System.exit(1);
1058                                                    }
1059                                                    oa[0] = factory.create(stream, null);
1060                                            } else {
1061                                                    File file = new File(args[0]);
1062                                                    if (!file.exists()) {
1063                                                            System.err.println("File does not exist or not a file.");
1064                                                            System.exit(1);
1065                                                    }
1066                                                    if (!file.isFile()) {
1067                                                            System.err.println("Not a file.");
1068                                                            System.exit(1);
1069                                                    }
1070                                                    oa[0] = factory.create(file, null);
1071                                            }
1072    
1073                                            if (oa[0] instanceof Component) {
1074                                                    ((Component) oa[0]).start();
1075                                            }
1076                                            
1077                                            if (oa[0] instanceof Restartable) {
1078                                                    ((Restartable) oa[0]).setRestartCommand(this);
1079                                            }
1080                                            
1081                                            try {
1082                                                    if (oa[0] instanceof Context) {
1083                                                            Context container=(Context) oa[0];
1084                                                            for (int i=1; i<args.length; i++) {
1085                                                                    Object toExecute=container.get(args[i]);
1086                                                                    if (toExecute instanceof Command) {
1087                                                                            ((Command) toExecute).execute(args);
1088                                                                    } else if (toExecute==null) {                                           
1089                                                                            System.err.print("[WARN] Name not found: " +args[i]);
1090                                                                    } else {
1091                                                                            System.err.print("[WARN] Not executable: (" +args[i]+") "+toExecute.getClass().getName());                                              
1092                                                                    }
1093                                                            }                                       
1094                                                    } else if (oa[0] instanceof Command) {
1095                                                            ((Command) oa[0]).execute(args);
1096                                                    }
1097                                            } finally {                     
1098                                                    if (oa[0] instanceof Component) {
1099                                                            if (!stopInHook) {
1100                                                                    ((Component) oa[0]).stop();
1101                                                            }
1102                                                    }
1103                                            }
1104                                    } catch (MalformedURLException e) {
1105                                            System.err.println("Bad configuration URL: "+args[0]);
1106                                            System.exit(2);
1107                                    } catch (Exception e) {
1108                                            e.printStackTrace();
1109                                            if (attempt > 1) {   
1110                                                    if (oa[0] instanceof Component) {
1111                                                            try {
1112                                                                    ((Component) oa[0]).stop();
1113                                                            } catch (Exception ex) {
1114                                                                    System.err.println("Cannot stop component before restart: "+e);
1115                                                                    ex.printStackTrace();
1116                                                            }
1117                                                    }
1118                                                    new Thread(this, "Restart thread "+getAttempt()).start(); // Use a new thread to avoid stack overflowing in the case of too many attempts.
1119                                            } else {                                        
1120                                                    System.exit(3);
1121                                            }
1122                                    }
1123                            }
1124    
1125                            public int getAttempt() {
1126                                    return attempt;
1127                            }
1128    
1129                            public long getRestartDelay() {
1130                                    String rd = System.getProperty(RESTART_DELAY_PROPERTY);
1131                                    if (rd!=null) {
1132                                            try {
1133                                                    return Long.parseLong(rd);
1134                                            } catch (NumberFormatException e) {
1135                                                    // Ignore
1136                                            }
1137                                    }
1138                                    
1139                                    return DEFAULT_RESTART_DELAY;
1140                            }
1141                    };
1142                    
1143                    run.run();
1144                    
1145            }
1146            
1147            /**
1148             * Loads providers for a given service. Providers are loaded anew at every
1149             * invocation of this method.
1150             * @param <T>
1151             * @param service Service class.
1152             * @param classLoader Class loader
1153             * @return Iterator of providers of given class.
1154             * @throws ConfigurationException
1155             */
1156            public static Iterator loadProviders(final Class service, final ClassLoader classLoader) {
1157                    final String resName = CONFIG_RESOURCE_PREFIX+service.getName()+XML_EXTENSION;
1158                    return new Iterator() {
1159                            
1160                            private Enumeration resources;
1161                            DomConfigFactory factory;
1162                            
1163                            private synchronized Enumeration getResources() {
1164                                    if (resources==null) {
1165                                            try {
1166                                                    if (classLoader==null) {// System classes
1167                                                            ClassLoader alternativeClassLoader = DuckConverterFactory.getChildClassLoader(service.getClassLoader(), this.getClass().getClassLoader());
1168                                                            if (alternativeClassLoader==null) {
1169                                                                    alternativeClassLoader = this.getClass().getClassLoader();
1170                                                            }
1171                                                            resources = alternativeClassLoader==null ? ClassLoader.getSystemResources(resName) : alternativeClassLoader.getResources(resName);                                                              
1172                                                            factory = new DomConfigFactory(alternativeClassLoader);
1173                                                    } else {
1174                                                            resources = classLoader.getResources(resName);
1175                                                            factory = new DomConfigFactory(classLoader);
1176                                                    }
1177                                                            
1178                                            } catch (IOException e) {
1179                                                    throw new RuntimeConfigurationException(e);
1180                                            }                                       
1181                                    }
1182                                    return resources;
1183                            }
1184    
1185                            public boolean hasNext() {
1186                                    return getResources().hasMoreElements();
1187                            }
1188    
1189                            public Object next() {
1190                                    try {
1191                                            return factory.create((URL) getResources().nextElement(), null);
1192                                    } catch (Exception e) {
1193                                            throw new RuntimeConfigurationException(e);
1194                                    }
1195                            }
1196    
1197                            public void remove() {
1198                                    throw new UnsupportedOperationException("This operation is not supported");                             
1199                            }
1200                            
1201                    };
1202                    
1203            }
1204    
1205    }