View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.xml;
15  
16  import java.io.IOException;
17  import java.io.InputStream;
18  import java.io.StringReader;
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.Field;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.lang.reflect.Modifier;
24  import java.net.InetAddress;
25  import java.net.MalformedURLException;
26  import java.net.URL;
27  import java.net.UnknownHostException;
28  import java.security.AccessControlException;
29  import java.security.AccessController;
30  import java.security.PrivilegedAction;
31  import java.util.HashMap;
32  import java.util.Iterator;
33  import java.util.Map;
34  import java.util.Properties;
35  
36  import org.eclipse.jetty.util.LazyList;
37  import org.eclipse.jetty.util.Loader;
38  import org.eclipse.jetty.util.TypeUtil;
39  import org.eclipse.jetty.util.component.LifeCycle;
40  import org.eclipse.jetty.util.log.Log;
41  import org.eclipse.jetty.util.resource.Resource;
42  import org.xml.sax.InputSource;
43  import org.xml.sax.SAXException;
44  
45  /* ------------------------------------------------------------ */
46  /**
47   * Configure Objects from XML. This class reads an XML file conforming to the configure.dtd DTD and
48   * uses it to configure and object by calling set, put or other methods on the object.
49   *
50   * 
51   */
52  public class XmlConfiguration
53  {
54  
55      private static Class[] __primitives = { Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE,
56              Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE};
57  
58      private static Class[] __primitiveHolders = { Boolean.class, Character.class, Byte.class,
59              Short.class, Integer.class, Long.class, Float.class, Double.class, Void.class};
60      private static final Integer ZERO=new Integer(0);
61      
62      /* ------------------------------------------------------------ */
63      private static XmlParser __parser;
64      private XmlParser.Node _config;
65      private Map _idMap = new HashMap();
66      private Map _propertyMap = new HashMap();
67  
68      /* ------------------------------------------------------------ */
69      private synchronized static void initParser() throws IOException
70      {
71          if (__parser != null) return;
72  
73          __parser = new XmlParser();
74          try
75          {
76              URL configURL = Loader.getResource(XmlConfiguration.class, "org/eclipse/jetty/xml/configure_6_0.dtd", true);
77              __parser.redirectEntity("configure.dtd", configURL);
78              __parser.redirectEntity("configure_1_0.dtd", configURL);
79              __parser.redirectEntity("configure_1_1.dtd", configURL);
80              __parser.redirectEntity("configure_1_2.dtd", configURL);
81              __parser.redirectEntity("configure_1_3.dtd", configURL);
82              __parser.redirectEntity("configure_6_0.dtd", configURL);
83              
84              __parser.redirectEntity("http://jetty.mortbay.org/configure.dtd", configURL);
85              __parser.redirectEntity("http://jetty.eclipse.org/configure.dtd", configURL);
86              __parser.redirectEntity("http://www.eclipse.org/jetty/configure.dtd", configURL);
87              
88              __parser.redirectEntity("-//Mort Bay Consulting//DTD Configure//EN", configURL);
89              __parser.redirectEntity("-//Jetty//Configure//EN", configURL);
90          }
91          catch (ClassNotFoundException e)
92          {
93              Log.warn(e.toString());
94              Log.debug(e);
95          }
96      }
97  
98      /* ------------------------------------------------------------ */
99      /**
100      * Constructor. Reads the XML configuration file.
101      *
102      * @param configuration
103      */
104     public XmlConfiguration(URL configuration) throws SAXException, IOException
105     {
106         initParser();
107         synchronized (__parser)
108         {
109             _config = __parser.parse(configuration.toString());
110         }
111     }
112 
113     /* ------------------------------------------------------------ */
114     /**
115      * Constructor.
116      *
117      * @param configuration String of XML configuration commands excluding the normal XML preamble.
118      *            The String should start with a " <Configure ...." element.
119      * @exception SAXException
120      * @exception IOException
121      */
122     public XmlConfiguration(String configuration) throws SAXException, IOException
123     {
124         initParser();
125         configuration = "<?xml version=\"1.0\"  encoding=\"ISO-8859-1\"?>\n<!DOCTYPE Configure PUBLIC \"-//Mort Bay Consulting//DTD Configure 1.2//EN\" \"http://jetty.eclipse.org/configure_1_2.dtd\">"
126                 + configuration;
127         InputSource source = new InputSource(new StringReader(configuration));
128         synchronized (__parser)
129         {
130             _config = __parser.parse(source);
131         }
132     }
133 
134     /* ------------------------------------------------------------ */
135     /**
136      * Constructor.
137      *
138      * @param configuration An input stream containing a complete e.g. configuration file
139      * @exception SAXException
140      * @exception IOException
141      */
142     public XmlConfiguration(InputStream configuration) throws SAXException, IOException
143     {
144         initParser();
145         InputSource source = new InputSource(configuration);
146         synchronized (__parser)
147         {
148             _config = __parser.parse(source);
149         }
150     }
151 
152     /* ------------------------------------------------------------ */
153     public Map getIdMap()
154     {
155         return _idMap;
156     }
157     
158     /* ------------------------------------------------------------ */
159     public void setIdMap(Map map)
160     {
161         _idMap=map;
162     }
163 
164     /* ------------------------------------------------------------ */
165     public void setProperties (Map map)
166     {
167         _propertyMap = map;
168     }
169 
170     /* ------------------------------------------------------------ */
171     public Map getProperties ()
172     {
173         return _propertyMap;
174     }
175     
176     /* ------------------------------------------------------------ */
177     /**
178      * Configure an object. If the object is of the approprate class, the XML configuration script
179      * is applied to the object.
180      *
181      * @param obj The object to be configured.
182      * @exception Exception
183      */
184     public void configure(Object obj) throws Exception
185     {
186         //Check the class of the object
187         Class oClass = nodeClass(_config);
188         if (!oClass.isInstance(obj))
189                 throw new IllegalArgumentException("Object is not of type " + oClass);
190         configure(obj, _config, 0);
191     }
192 
193     /* ------------------------------------------------------------ */
194     /**
195      * Configure an object.  If the configuration has an ID, an object is looked up
196      * by ID and it's type check.  Otherwise a new object is created.
197      * 
198      * @return The newly created configured object.
199      * @exception Exception
200      */
201     public Object configure() throws Exception
202     {
203         Class oClass = nodeClass(_config);
204         
205         String id = _config.getAttribute("id");
206         Object obj = id==null?null:_idMap.get(id);
207         
208         if (obj==null && oClass !=null)
209             obj = oClass.newInstance();
210         
211         if (oClass!=null && !oClass.isInstance(obj))
212             throw new ClassCastException(oClass.toString());
213         
214         configure(obj, _config, 0);
215         return obj;
216     }
217 
218     /* ------------------------------------------------------------ */
219     private Class nodeClass(XmlParser.Node node) throws ClassNotFoundException
220     {
221         String className = node.getAttribute("class");
222         if (className == null) return null;
223 
224         return Loader.loadClass(XmlConfiguration.class, className,true);
225     }
226 
227     /* ------------------------------------------------------------ */
228     /*
229      * Recursive configuration step. This method applies the remaining Set, Put and Call elements to
230      * the current object. @param obj @param cfg @param i @exception Exception
231      */
232     private void configure(Object obj, XmlParser.Node cfg, int i) throws Exception
233     {
234         String id = cfg.getAttribute("id");
235         if (id!=null)
236             _idMap.put(id,obj);
237 
238         for (; i < cfg.size(); i++)
239         {
240             Object o = cfg.get(i);
241             if (o instanceof String) continue;
242             XmlParser.Node node = (XmlParser.Node) o;
243 
244             try
245             {
246                 String tag = node.getTag();
247                 if ("Set".equals(tag))
248                     set(obj, node);
249                 else if ("Put".equals(tag))
250                     put(obj, node);
251                 else if ("Call".equals(tag))
252                     call(obj, node);
253                 else if ("Get".equals(tag))
254                     get(obj, node);
255                 else if ("New".equals(tag))
256                     newObj(obj, node);
257                 else if ("Array".equals(tag))
258                     newArray(obj, node);
259                 else if ("Ref".equals(tag))
260                     refObj(obj, node);
261                 else if ("Property".equals(tag))
262                     propertyObj(obj, node);
263                 else
264                     throw new IllegalStateException("Unknown tag: " + tag);
265             }
266             catch (Exception e)
267             {
268                 Log.warn("Config error at " + node, e.toString());
269                 throw e;
270             }
271         }
272     }
273 
274     /* ------------------------------------------------------------ */
275     /*
276      * Call a set method. This method makes a best effort to find a matching set method. The type of
277      * the value is used to find a suitable set method by 1. Trying for a trivial type match. 2.
278      * Looking for a native type match. 3. Trying all correctly named methods for an auto
279      * conversion. 4. Attempting to construct a suitable value from original value. @param obj
280      * @param node
281      */
282     private void set(Object obj, XmlParser.Node node) throws Exception
283     {
284         String attr = node.getAttribute("name");
285         String name = "set" + attr.substring(0, 1).toUpperCase() + attr.substring(1);
286         Object value = value(obj, node);
287         Object[] arg = { value};
288 
289         Class oClass = nodeClass(node);
290         if (oClass != null)
291             obj = null;
292         else
293             oClass = obj.getClass();
294 
295         Class[] vClass = { Object.class};
296         if (value != null) vClass[0] = value.getClass();
297 
298         if (Log.isDebugEnabled())
299                 Log.debug("XML "+(obj!=null?obj.toString():oClass.getName()) + "." + name + "(" + value + ")");
300 
301         // Try for trivial match
302         try
303         {
304             Method set = oClass.getMethod(name, vClass);
305             set.invoke(obj, arg);
306             return;
307         }
308         catch (IllegalArgumentException e)
309         {
310             Log.ignore(e);
311         }
312         catch (IllegalAccessException e)
313         {
314             Log.ignore(e);
315         }
316         catch (NoSuchMethodException e)
317         {
318             Log.ignore(e);
319         }
320 
321         // Try for native match
322         try
323         {
324             Field type = vClass[0].getField("TYPE");
325             vClass[0] = (Class) type.get(null);
326             Method set = oClass.getMethod(name, vClass);
327             set.invoke(obj, arg);
328             return;
329         }
330         catch (NoSuchFieldException e)
331         {
332             Log.ignore(e);
333         }
334         catch (IllegalArgumentException e)
335         {
336             Log.ignore(e);
337         }
338         catch (IllegalAccessException e)
339         {
340             Log.ignore(e);
341         }
342         catch (NoSuchMethodException e)
343         {
344             Log.ignore(e);
345         }
346 
347         // Try a field
348         try
349         {
350             Field field = oClass.getField(attr);
351             if (Modifier.isPublic(field.getModifiers()))
352             {
353                 field.set(obj, value);
354                 return;
355             }
356         }
357         catch (NoSuchFieldException e)
358         {
359             Log.ignore(e);
360         }
361 
362         // Search for a match by trying all the set methods
363         Method[] sets = oClass.getMethods();
364         Method set = null;
365         for (int s = 0; sets != null && s < sets.length; s++)
366         {
367             if (name.equals(sets[s].getName()) && sets[s].getParameterTypes().length == 1)
368             {
369                 // lets try it
370                 try
371                 {
372                     set = sets[s];
373                     sets[s].invoke(obj, arg);
374                     return;
375                 }
376                 catch (IllegalArgumentException e)
377                 {
378                     Log.ignore(e);
379                 }
380                 catch (IllegalAccessException e)
381                 {
382                     Log.ignore(e);
383                 }
384             }
385         }
386 
387         // Try converting the arg to the last set found.
388         if (set != null)
389         {
390             try
391             {
392                 Class sClass = set.getParameterTypes()[0];
393                 if (sClass.isPrimitive())
394                 {
395                     for (int t = 0; t < __primitives.length; t++)
396                     {
397                         if (sClass.equals(__primitives[t]))
398                         {
399                             sClass = __primitiveHolders[t];
400                             break;
401                         }
402                     }
403                 }
404                 Constructor cons = sClass.getConstructor(vClass);
405                 arg[0] = cons.newInstance(arg);
406                 set.invoke(obj, arg);
407                 return;
408             }
409             catch (NoSuchMethodException e)
410             {
411                 Log.ignore(e);
412             }
413             catch (IllegalAccessException e)
414             {
415                 Log.ignore(e);
416             }
417             catch (InstantiationException e)
418             {
419                 Log.ignore(e);
420             }
421         }
422 
423         // No Joy
424         throw new NoSuchMethodException(oClass + "." + name + "(" + vClass[0] + ")");
425     }
426 
427     /* ------------------------------------------------------------ */
428     /*
429      * Call a put method.
430      *
431      * @param obj @param node
432      */
433     private void put(Object obj, XmlParser.Node node) throws Exception
434     {
435         if (!(obj instanceof Map))
436                 throw new IllegalArgumentException("Object for put is not a Map: " + obj);
437         Map map = (Map) obj;
438 
439         String name = node.getAttribute("name");
440         Object value = value(obj, node);
441         map.put(name, value);
442         if (Log.isDebugEnabled()) Log.debug("XML "+obj + ".put(" + name + "," + value + ")");
443     }
444 
445     /* ------------------------------------------------------------ */
446     /*
447      * Call a get method. Any object returned from the call is passed to the configure method to
448      * consume the remaining elements. @param obj @param node @return @exception Exception
449      */
450     private Object get(Object obj, XmlParser.Node node) throws Exception
451     {
452         Class oClass = nodeClass(node);
453         if (oClass != null)
454             obj = null;
455         else
456             oClass = obj.getClass();
457 
458         String name = node.getAttribute("name");
459         String id = node.getAttribute("id");
460         if (Log.isDebugEnabled()) Log.debug("XML get " + name);
461 
462         try
463         {
464             // try calling a getXxx method.
465             Method method = oClass.getMethod("get" + name.substring(0, 1).toUpperCase()
466                     + name.substring(1), (java.lang.Class[]) null);
467             obj = method.invoke(obj, (java.lang.Object[]) null);
468             configure(obj, node, 0);
469         }
470         catch (NoSuchMethodException nsme)
471         {
472             try
473             {
474                 Field field = oClass.getField(name);
475                 obj = field.get(obj);
476                 configure(obj, node, 0);
477             }
478             catch (NoSuchFieldException nsfe)
479             {
480                 throw nsme;
481             }
482         }
483         if (id != null) _idMap.put(id, obj);
484         return obj;
485     }
486 
487     /* ------------------------------------------------------------ */
488     /*
489      * Call a method. A method is selected by trying all methods with matching names and number of
490      * arguments. Any object returned from the call is passed to the configure method to consume the
491      * remaining elements. Note that if this is a static call we consider only methods declared
492      * directly in the given class. i.e. we ignore any static methods in superclasses. @param obj
493      * @param node @return @exception Exception
494      */
495     private Object call(Object obj, XmlParser.Node node) throws Exception
496     {
497         String id = node.getAttribute("id");
498         Class oClass = nodeClass(node);
499         if (oClass != null)
500             obj = null;
501         else if (obj != null) oClass = obj.getClass();
502         if (oClass == null) throw new IllegalArgumentException(node.toString());
503 
504         int size = 0;
505         int argi = node.size();
506         for (int i = 0; i < node.size(); i++)
507         {
508             Object o = node.get(i);
509             if (o instanceof String) continue;
510             if (!((XmlParser.Node) o).getTag().equals("Arg"))
511             {
512                 argi = i;
513                 break;
514             }
515             size++;
516         }
517 
518         Object[] arg = new Object[size];
519         for (int i = 0, j = 0; j < size; i++)
520         {
521             Object o = node.get(i);
522             if (o instanceof String) continue;
523             arg[j++] = value(obj, (XmlParser.Node) o);
524         }
525 
526         String method = node.getAttribute("name");
527         if (Log.isDebugEnabled()) Log.debug("XML call " + method);
528 
529         // Lets just try all methods for now
530         Method[] methods = oClass.getMethods();
531         for (int c = 0; methods != null && c < methods.length; c++)
532         {
533             if (!methods[c].getName().equals(method)) continue;
534             if (methods[c].getParameterTypes().length != size) continue;
535             if (Modifier.isStatic(methods[c].getModifiers()) != (obj == null)) continue;
536             if ((obj == null) && methods[c].getDeclaringClass() != oClass) continue;
537 
538             Object n = null;
539             boolean called = false;
540             try
541             {
542                 n = methods[c].invoke(obj, arg);
543                 called = true;
544             }
545             catch (IllegalAccessException e)
546             {
547                 Log.ignore(e);
548             }
549             catch (IllegalArgumentException e)
550             {
551                 Log.ignore(e);
552             }
553             if (called)
554             {
555                 if (id != null) _idMap.put(id, n);
556                 configure(n, node, argi);
557                 return n;
558             }
559         }
560 
561         throw new IllegalStateException("No Method: " + node + " on " + oClass);
562     }
563 
564     /* ------------------------------------------------------------ */
565     /*
566      * Create a new value object.
567      *
568      * @param obj @param node @return @exception Exception
569      */
570     private Object newObj(Object obj, XmlParser.Node node) throws Exception
571     {
572         Class oClass = nodeClass(node);
573         String id = node.getAttribute("id");
574         int size = 0;
575         int argi = node.size();
576         for (int i = 0; i < node.size(); i++)
577         {
578             Object o = node.get(i);
579             if (o instanceof String) continue;
580             if (!((XmlParser.Node) o).getTag().equals("Arg"))
581             {
582                 argi = i;
583                 break;
584             }
585             size++;
586         }
587 
588         Object[] arg = new Object[size];
589         for (int i = 0, j = 0; j < size; i++)
590         {
591             Object o = node.get(i);
592             if (o instanceof String) continue;
593             arg[j++] = value(obj, (XmlParser.Node) o);
594         }
595 
596         if (Log.isDebugEnabled()) Log.debug("XML new " + oClass);
597 
598         // Lets just try all constructors for now
599         Constructor[] constructors = oClass.getConstructors();
600         for (int c = 0; constructors != null && c < constructors.length; c++)
601         {
602             if (constructors[c].getParameterTypes().length != size) continue;
603 
604             Object n = null;
605             boolean called = false;
606             try
607             {
608                 n = constructors[c].newInstance(arg);
609                 called = true;
610             }
611             catch (IllegalAccessException e)
612             {
613                 Log.ignore(e);
614             }
615             catch (InstantiationException e)
616             {
617                 Log.ignore(e);
618             }
619             catch (IllegalArgumentException e)
620             {
621                 Log.ignore(e);
622             }
623             if (called)
624             {
625                 if (id != null) _idMap.put(id, n);
626                 configure(n, node, argi);
627                 return n;
628             }
629         }
630 
631         throw new IllegalStateException("No Constructor: " + node + " on " + obj);
632     }
633 
634     /* ------------------------------------------------------------ */
635     /*
636      * Reference an id value object.
637      *
638      * @param obj @param node @return @exception NoSuchMethodException @exception
639      * ClassNotFoundException @exception InvocationTargetException
640      */
641     private Object refObj(Object obj, XmlParser.Node node) throws Exception
642     {
643         String id = node.getAttribute("id");
644         obj = _idMap.get(id);
645         if (obj == null) throw new IllegalStateException("No object for id=" + id);
646         configure(obj, node, 0);
647         return obj;
648     }
649 
650 
651     /* ------------------------------------------------------------ */
652     /*
653      * Create a new array object.
654      *
655      */
656     private Object newArray(Object obj, XmlParser.Node node) throws Exception
657     {
658 
659         // Get the type
660         Class aClass = java.lang.Object.class;
661         String type = node.getAttribute("type");
662         final String id = node.getAttribute("id");
663         if (type != null)
664         {
665             aClass = TypeUtil.fromName(type);
666             if (aClass == null)
667             {
668                 if ("String".equals(type))
669                     aClass = java.lang.String.class;
670                 else if ("URL".equals(type))
671                     aClass = java.net.URL.class;
672                 else if ("InetAddress".equals(type))
673                     aClass = java.net.InetAddress.class;
674                 else
675                     aClass = Loader.loadClass(XmlConfiguration.class, type,true);
676             }
677         }
678 
679         Object al=null;
680         
681         Iterator iter = node.iterator("Item");
682         while(iter.hasNext())
683         {
684             XmlParser.Node item= (XmlParser.Node)iter.next();
685             String nid = item.getAttribute("id");
686             Object v = value(obj, item);
687             al=LazyList.add(al,(v==null&&aClass.isPrimitive())?ZERO:v);
688             if (nid != null) 
689                 _idMap.put(nid, v);
690         }
691         
692         Object array =  LazyList.toArray(al,aClass);
693         if (id != null) 
694             _idMap.put(id, array);
695         return array; 
696     }
697     
698     /* ------------------------------------------------------------ */
699     /*
700      * Create a new map object.
701      *
702      */
703     private Object newMap(Object obj, XmlParser.Node node) throws Exception
704     {
705         String id = node.getAttribute("id");
706 
707         Map map = new HashMap();
708         if (id != null) _idMap.put(id, map);
709 
710         for (int i = 0; i < node.size(); i++)
711         {
712             Object o = node.get(i);
713             if (o instanceof String) continue;
714             XmlParser.Node entry = (XmlParser.Node) o;
715             if (!entry.getTag().equals("Entry")) throw new IllegalStateException("Not an Entry");
716             
717             
718             XmlParser.Node key=null;
719             XmlParser.Node value=null;
720 
721             for (int j = 0; j < entry.size(); j++)
722             {
723                 o = entry.get(j);
724                 if (o instanceof String) continue;
725                 XmlParser.Node item = (XmlParser.Node) o;
726                 if (!item.getTag().equals("Item")) throw new IllegalStateException("Not an Item");
727                 if (key==null) 
728                     key=item;
729                 else
730                     value=item;
731             }
732             
733             if (key==null || value==null)
734                 throw new IllegalStateException("Missing Item in Entry");
735             String kid = key.getAttribute("id");
736             String vid = value.getAttribute("id");
737              
738             Object k = value(obj, key);
739             Object v = value(obj, value);
740             map.put(k,v);
741             
742             if (kid != null) _idMap.put(kid, k);
743             if (vid != null) _idMap.put(vid, v);
744         }
745 
746         return map;
747     }
748 
749     /* ------------------------------------------------------------ */
750     /*
751      * Create a new value object.
752      *
753      * @param obj @param node @return @exception Exception
754      */
755     private Object propertyObj(Object obj, XmlParser.Node node) throws Exception
756     {
757         String id = node.getAttribute("id");
758         String name = node.getAttribute("name");
759         Object defval = node.getAttribute("default");
760         Object prop=null;
761         if (_propertyMap!=null && _propertyMap.containsKey(name))
762         {
763             prop=_propertyMap.get(name);
764         }
765         else if (defval != null)
766             prop=defval;
767 
768         if (id != null) 
769             _idMap.put(id, prop);
770         if (prop!=null)
771             configure(prop, node, 0);
772         return prop;
773     }
774     
775     /* ------------------------------------------------------------ */
776     /*
777      * Get the value of an element. If no value type is specified, then white space is trimmed out
778      * of the value. If it contains multiple value elements they are added as strings before being
779      * converted to any specified type. @param node
780      */
781     private Object value(Object obj, XmlParser.Node node) throws Exception
782     {
783         Object value = null;
784 
785         // Get the type
786         String type = node.getAttribute("type");
787 
788         // Try a ref lookup
789         String ref = node.getAttribute("ref");
790         if (ref != null)
791         {
792             value = _idMap.get(ref);
793         }
794         else
795         {
796             // handle trivial case
797             if (node.size() == 0)
798             {
799                 if ("String".equals(type)) return "";
800                 return null;
801             }
802 
803             // Trim values
804             int first = 0;
805             int last = node.size() - 1;
806 
807             // Handle default trim type
808             if (type == null || !"String".equals(type))
809             {
810                 // Skip leading white
811                 Object item = null;
812                 while (first <= last)
813                 {
814                     item = node.get(first);
815                     if (!(item instanceof String)) break;
816                     item = ((String) item).trim();
817                     if (((String) item).length() > 0) break;
818                     first++;
819                 }
820 
821                 // Skip trailing white
822                 while (first < last)
823                 {
824                     item = node.get(last);
825                     if (!(item instanceof String)) break;
826                     item = ((String) item).trim();
827                     if (((String) item).length() > 0) break;
828                     last--;
829                 }
830 
831                 // All white, so return null
832                 if (first > last) return null;
833             }
834 
835             if (first == last)
836                 //  Single Item value
837                 value = itemValue(obj, node.get(first));
838             else
839             {
840                 // Get the multiple items as a single string
841                 StringBuilder buf = new StringBuilder();
842                 for (int i = first; i <= last; i++)
843                 {
844                     Object item = node.get(i);
845                     buf.append(itemValue(obj, item));
846                 }
847                 value = buf.toString();
848             }
849         }
850 
851         // Untyped or unknown
852         if (value == null)
853         {
854             if ("String".equals(type)) return "";
855             return null;
856         }
857 
858         // Try to type the object
859         if (type == null)
860         {
861             if (value != null && value instanceof String) return ((String) value).trim();
862             return value;
863         }
864 
865         if ("String".equals(type) || "java.lang.String".equals(type)) return value.toString();
866 
867         Class pClass = TypeUtil.fromName(type);
868         if (pClass != null) return TypeUtil.valueOf(pClass, value.toString());
869 
870         if ("URL".equals(type) || "java.net.URL".equals(type))
871         {
872             if (value instanceof URL) return value;
873             try
874             {
875                 return new URL(value.toString());
876             }
877             catch (MalformedURLException e)
878             {
879                 throw new InvocationTargetException(e);
880             }
881         }
882 
883         if ("InetAddress".equals(type) || "java.net.InetAddress".equals(type))
884         {
885             if (value instanceof InetAddress) return value;
886             try
887             {
888                 return InetAddress.getByName(value.toString());
889             }
890             catch (UnknownHostException e)
891             {
892                 throw new InvocationTargetException(e);
893             }
894         }
895 
896         throw new IllegalStateException("Unknown type " + type);
897     }
898 
899     /* ------------------------------------------------------------ */
900     /*
901      * Get the value of a single element. @param obj @param item @return @exception Exception
902      */
903     private Object itemValue(Object obj, Object item) throws Exception
904     {
905         // String value
906         if (item instanceof String) return item;
907 
908         XmlParser.Node node = (XmlParser.Node) item;
909         String tag = node.getTag();
910         if ("Call".equals(tag)) return call(obj, node);
911         if ("Get".equals(tag)) return get(obj, node);
912         if ("New".equals(tag)) return newObj(obj, node);
913         if ("Ref".equals(tag)) return refObj(obj, node);
914         if ("Array".equals(tag)) return newArray(obj, node);
915         if ("Map".equals(tag)) return newMap(obj, node);
916         if ("Property".equals(tag)) return propertyObj(obj,node);
917 
918         if ("SystemProperty".equals(tag))
919         {
920             String name = node.getAttribute("name");
921             String defaultValue = node.getAttribute("default");
922             return System.getProperty(name, defaultValue);
923         }
924         
925         Log.warn("Unknown value tag: " + node, new Throwable());
926         return null;
927     }
928 
929     /* ------------------------------------------------------------ */
930     /* ------------------------------------------------------------ */
931     /* ------------------------------------------------------------ */
932     /* ------------------------------------------------------------ */
933     /** 
934      * Run the XML configurations as a main application.
935      * The command line is used to obtain properties files (must be named '*.properties') and XmlConfiguration
936      * files.
937      * <p>
938      * Any property file on the command line is added to a combined Property instance that is passed to
939      * each configuration file via {@link XmlConfiguration#setProperties(Map)}.
940      * <p>
941      * Each configuration file on the command line is used to create a new XmlConfiguration instance and the
942      * {@link XmlConfiguration#configure()} method is used to create the configured object.  If the resulting 
943      * object is an instance of {@link LifeCycle}, then it is started.
944      * <p>
945      * Any IDs created in a configuration are passed to the next configuration file on the command line using
946      * {@link #getIdMap()} and {@link #setIdMap(Map)}. This allows objects with IDs created in one config file to
947      * be referenced in subsequent config files on the command line.
948      * 
949      * @param args array of property and xml configuration filenames or {@link Resource}s.
950      */
951     @SuppressWarnings( "unchecked" )
952     public static void main( final String[] args )
953     {
954 
955         AccessController.doPrivileged( new PrivilegedAction()
956         {
957             public Object run()
958             {
959                 try
960                 {
961 
962                     Properties properties = new Properties();
963                     XmlConfiguration last = null;
964                     Object[] obj = new Object[args.length];
965                     for ( int i = 0; i < args.length; i++ )
966                     {
967                         if ( args[i].toLowerCase().endsWith( ".properties" ) )
968                         {
969                             properties.load( Resource.newResource( args[i] ).getInputStream() );
970                         }
971                         else
972                         {
973                             XmlConfiguration configuration =
974                                 new XmlConfiguration( Resource.newResource( args[i] ).getURL() );
975                             if ( last != null )
976                                 configuration.getIdMap().putAll( last.getIdMap() );
977                             if ( properties.size() > 0 )
978                                 configuration.setProperties( properties );
979                             obj[i] = configuration.configure();
980                             last = configuration;
981                         }
982                     }
983 
984                     for ( int i = 0; i < args.length; i++ )
985                     {
986                         if ( obj[i] instanceof LifeCycle )
987                         {
988                             LifeCycle lc = (LifeCycle) obj[i];
989                             if ( !lc.isRunning() )
990                                 lc.start();
991                         }
992                     }
993                 }
994                 catch (AccessControlException ace)
995                 {
996                     ace.printStackTrace(System.err);
997                 }
998                 catch ( Exception e )
999                 {
1000                     Log.warn( Log.EXCEPTION, e );
1001                 }
1002                 return null;
1003             }
1004         } );
1005     }
1006     
1007 }
1008