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