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