DomConfigFactory.java

biz/hammurapi/config/DomConfigFactory.java

Violations

Inspector Message Severity Location
Java Inspector 048 Copyrights information should be present in each file. 1
Java Inspector 070-B Cyclomatic complexity is too high: 68, maximum allowed is 20 1 397:9
Java Inspector 070-B Cyclomatic complexity is too high: 85, maximum allowed is 20 1 720:9
Java Inspector 070-B Cyclomatic complexity is too high: 24, maximum allowed is 20 1 1053:25
Java Inspector 035 Code length is more than 200 2 720:9
Java Inspector 049 Use a Collection instead of arrays Object[] 2 424:38
Java Inspector 049 Use a Collection instead of arrays Object[] 2 438:39
Java Inspector 049 Use a Collection instead of arrays Object[] 2 721:23
Java Inspector 049 Use a Collection instead of arrays Object[] 2 737:23
Java Inspector 049 Use a Collection instead of arrays Object[] 2 782:23
Java Inspector 049 Use a Collection instead of arrays Object[] 2 997:22
Java Inspector 049 Use a Collection instead of arrays Object[] 2 1030:17
Java Inspector 068 Do not use System.out and System.err to output logging messages. Use loggers instead. 2 1025:43
Java Inspector 068 Do not use System.out and System.err to output logging messages. Use loggers instead. 2 1040:91
Java Inspector 068 Do not use System.out and System.err to output logging messages. Use loggers instead. 2 1043:83
Java Inspector 068 Do not use System.out and System.err to output logging messages. Use loggers instead. 2 1057:65
Java Inspector 068 Do not use System.out and System.err to output logging messages. Use loggers instead. 2 1075:75
Java Inspector 068 Do not use System.out and System.err to output logging messages. Use loggers instead. 2 1082:75
Java Inspector 068 Do not use System.out and System.err to output logging messages. Use loggers instead. 2 1086:75
Java Inspector 068 Do not use System.out and System.err to output logging messages. Use loggers instead. 2 1108:89
Java Inspector 068 Do not use System.out and System.err to output logging messages. Use loggers instead. 2 1110:89
Java Inspector 068 Do not use System.out and System.err to output logging messages. Use loggers instead. 2 1124:59
Java Inspector 068 Do not use System.out and System.err to output logging messages. Use loggers instead. 2 1133:83
Java Inspector 070-A Cyclomatic complexity is too high: 68, maximum allowed is 12 2 397:9
Java Inspector 070-A Cyclomatic complexity is too high: 85, maximum allowed is 12 2 720:9
Java Inspector 070-A Cyclomatic complexity is too high: 24, maximum allowed is 12 2 1053:25
Java Inspector 081 Avoid static collections, they can grow in size over time. 2 202:9
Java Inspector 082 Parenthesis are redundant. 2 440:62
Java Inspector 082 Parenthesis are redundant. 2 643:44
Java Inspector 082 Parenthesis are redundant. 2 745:81
Java Inspector 082 Parenthesis are redundant. 2 790:81
Java Inspector 082 Parenthesis are redundant. 2 825:73
Java Inspector 082 Parenthesis are redundant. 2 865:73
Java Inspector 082 Parenthesis are redundant. 2 911:73
Java Inspector 082 Parenthesis are redundant. 2 962:73
Java Inspector 082 Parenthesis are redundant. 2 1043:109
Java Inspector 082 Parenthesis are redundant. 2 1057:124
Java Inspector 083 Do not use printStackTrace() for exception logging. 2 1041:90
Java Inspector 083 Do not use printStackTrace() for exception logging. 2 1061:75
Java Inspector 083 Do not use printStackTrace() for exception logging. 2 1127:58
Java Inspector 083 Do not use printStackTrace() for exception logging. 2 1134:83
Java Inspector 089 Undocumented field 2 192:9
Java Inspector 089 Undocumented field 2 193:9
Java Inspector 089 Undocumented field 2 195:9
Java Inspector 089 Undocumented field 2 196:9
Java Inspector 089 Undocumented field 2 197:9
Java Inspector 089 Undocumented field 2 198:9
Java Inspector 089 Undocumented field 2 199:9
Java Inspector 089 Undocumented field 2 202:9
Java Inspector 089 Constructor documentation is too short. It is only 2 words. Should be at least 3 words. 2 223:9
Java Inspector 089 Constructor documentation is too short. It is only 2 words. Should be at least 3 words. 2 230:9
Java Inspector 089 Undocumented parameter context 2 230:9
Java Inspector 089 Undocumented constructor 2 235:9
Java Inspector 089 Undocumented constructor 2 240:9
Java Inspector 089 Parameter node is not documented 2 252:9
Java Inspector 089 Method return value is not properly documented 2 252:9
Java Inspector 089 Undocumented exception ConfigurationException 2 262:9
Java Inspector 089 Undocumented exception IOException 2 262:9
Java Inspector 089 Undocumented exception ConfigurationException 2 274:9
Java Inspector 089 Undocumented exception IOException 2 274:9
Java Inspector 089 Parameter classAcceptor is not documented 2 308:9
Java Inspector 089 Parameter objectAcceptor is not documented 2 308:9
Java Inspector 089 Parameter in documentation is too short. It is only 1 words. Should be at least 3 words. 2 328:9
Java Inspector 089 Parameter in documentation is too short. It is only 1 words. Should be at least 3 words. 2 342:9
Java Inspector 089 Parameter classAcceptor is not documented 2 342:9
Java Inspector 089 Parameter objectAcceptor is not documented 2 342:9
Java Inspector 089 Parameter url documentation is too short. It is only 1 words. Should be at least 3 words. 2 376:9
Java Inspector 089 Parameter classAcceptor is not documented 2 376:9
Java Inspector 089 Parameter objectAcceptor is not documented 2 376:9
Java Inspector 089 Parameter node is not documented 2 397:9
Java Inspector 089 Parameter classAcceptor is not documented 2 397:9
Java Inspector 089 Parameter objectAcceptor is not documented 2 397:9
Java Inspector 089 Javadoc contains tag for non-existent parameter cxpa 2 397:9
Java Inspector 089 Constructor is not properly documented 2 607:17
Java Inspector 089 Parameter element is not documented 2 607:17
Java Inspector 089 Parameter instance is not documented 2 607:17
Java Inspector 089 Parameter instance is not documented 2 720:9
Java Inspector 089 Undocumented exception ConfigurationException 2 720:9
Java Inspector 089 Undocumented method 2 1022:9
Java Inspector 089 Undocumented method 2 1035:49
Java Inspector 089 Undocumented method 2 1053:25
Java Inspector 089 Undocumented method 2 1144:25
Java Inspector 089 Undocumented method 2 1148:25
Java Inspector 089 Javadoc contains tag for non-existent parameter 2 1175:9
Java Inspector 089 Javadoc contains tag for exception which method doesn't throw ConfigurationException 2 1175:9
Java Inspector 089 Undocumented method 2 1204:25
Java Inspector 089 Undocumented method 2 1208:25
Java Inspector 089 Undocumented method 2 1216:25
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 745:62
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 745:122
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 745:146
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 745:176
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 790:62
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 790:122
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 790:146
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 790:176
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 825:54
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 825:114
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 825:138
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 825:168
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 865:54
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 865:114
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 865:138
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 865:168
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 911:54
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 911:114
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 911:138
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 911:168
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 962:54
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 962:114
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 962:138
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 962:168
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 1043:145
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 1062:69
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 1125:53
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 1139:61
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 207:32
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 208:32
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 209:32
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 210:32
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 211:32
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 212:32
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 213:32
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 214:32
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 404:60
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 413:78
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 415:65
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 423:91
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 443:96
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 454:48
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 455:110
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 456:55
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 457:112
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 459:56
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 460:164
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 461:63
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 462:166
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 463:63
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 464:144
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 466:74
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 469:56
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 472:134
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 473:63
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 476:135
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 477:63
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 478:187
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 485:69
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 496:83
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 499:98
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 511:95
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 513:75
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 520:75
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 529:69
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 550:84
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 553:89
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 689:89
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 689:168
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 728:64
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 730:71
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 745:150
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 790:150
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 825:142
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 865:142
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 911:142
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 962:142
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1025:44
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1025:102
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1029:44
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1029:86
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1040:92
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1043:84
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1043:151
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1057:66
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1057:96
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1070:64
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1071:98
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1075:76
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1082:76
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1086:76
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1108:90
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1110:90
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1110:126
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1124:60
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1133:84
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1137:66
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 1217:73
Java Inspector 046 Empty statements 3 1153:75

Source code

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