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