1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.core.config;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.apache.commons.lang.text.StrLookup;
20 import org.apache.commons.lang.text.StrSubstitutor;
21 import org.apache.log4j.Logger;
22 import org.kuali.rice.core.util.RiceUtilities;
23 import org.kuali.rice.core.util.XmlJotter;
24 import org.w3c.dom.Document;
25 import org.w3c.dom.Element;
26 import org.w3c.dom.Node;
27 import org.w3c.dom.NodeList;
28 import org.xml.sax.SAXException;
29
30 import javax.xml.parsers.DocumentBuilderFactory;
31 import javax.xml.parsers.ParserConfigurationException;
32 import javax.xml.transform.TransformerException;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.util.LinkedHashMap;
36 import java.util.Map;
37 import java.util.Random;
38
39
40
41
42
43
44
45
46
47 public class ConfigParserImpl implements ConfigParser {
48
49 private static final Random RANDOM = new Random();
50
51 private static final Logger LOG = Logger.getLogger(ConfigParserImpl.class);
52 private static final String IMPORT_NAME = "config.location";
53 private static final String PARAM_NAME= "param";
54 private static final String NAME_ATTR = "name";
55 private static final String OVERRIDE_ATTR = "override";
56 private static final String RANDOM_ATTR = "random";
57 private static final String INDENT = " ";
58
59 public static final String ALTERNATE_BUILD_LOCATION_KEY = "alt.build.location";
60
61
62
63
64
65
66 private static class SystemPropertiesDelegatingStrLookup extends StrLookup {
67 private final Map map;
68 private SystemPropertiesDelegatingStrLookup(Map map) {
69 this.map = map;
70 }
71 @Override
72 public String lookup(String key) {
73 Object o = map.get(key);
74 if (o != null) {
75 return String.valueOf(o);
76 } else {
77 String s = System.getProperty(key);
78 if (s != null) {
79 return s;
80 } else {
81
82
83 return "";
84 }
85 }
86 }
87 }
88
89
90
91
92 public void parse(Map props, String[] locations) throws IOException {
93 LinkedHashMap params = new LinkedHashMap();
94 params.putAll(props);
95 parse(params, locations);
96 props.putAll(params);
97 }
98
99
100
101
102
103
104
105 protected void parse(LinkedHashMap<String, Object> params, String[] locations) throws IOException {
106 StrSubstitutor subs = new StrSubstitutor(new SystemPropertiesDelegatingStrLookup(params));
107 for (String location: locations) {
108 parse(params, location, subs, 0);
109 }
110 }
111
112
113
114
115
116
117
118
119 protected void parse(LinkedHashMap<String, Object> params, String location, StrSubstitutor subs, int depth) throws IOException {
120 InputStream configStream = RiceUtilities.getResourceAsStream(location);
121 if (configStream == null) {
122 LOG.warn("###############################");
123 LOG.warn("#");
124 LOG.warn("# Configuration file '" + location + "' not found!");
125 LOG.warn("#");
126 LOG.warn("###############################");
127 return;
128 }
129
130 final String prefix = StringUtils.repeat(INDENT, depth);
131 LOG.info(prefix + "+ Parsing config: " + location);
132
133 Document doc;
134 try {
135 doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(configStream);
136 if (LOG.isDebugEnabled()) {
137 LOG.debug("Contents of config " + location + ": \n" + XmlJotter.jotNode(doc, true));
138 }
139 } catch (SAXException se) {
140 IOException ioe = new IOException("Error parsing config resource: " + location);
141 ioe.initCause(se);
142 throw ioe;
143 } catch (ParserConfigurationException pce) {
144 IOException ioe = new IOException("Unable to obtain document builder");
145 ioe.initCause(pce);
146 throw ioe;
147 } finally {
148 configStream.close();
149 }
150
151 Element root = doc.getDocumentElement();
152
153
154 NodeList list = root.getChildNodes();
155 StringBuilder content = new StringBuilder();
156 for (int i = 0; i < list.getLength(); i++) {
157 Node node = list.item(i);
158 if (node.getNodeType() != Node.ELEMENT_NODE)
159 continue;
160 if (!PARAM_NAME.equals(node.getNodeName())) {
161 LOG.warn("Encountered non-param config node: " + node.getNodeName());
162 continue;
163 }
164 Element param = (Element) node;
165 String name = param.getAttribute(NAME_ATTR);
166 if (name == null) {
167 LOG.error("Unnamed parameter in config resource '" + location + "': " + XmlJotter.jotNode(param));
168 continue;
169 }
170 Boolean override = Boolean.TRUE;
171 String overrideVal = param.getAttribute(OVERRIDE_ATTR);
172 if (!StringUtils.isEmpty(overrideVal)) {
173 override = Boolean.valueOf(overrideVal);
174 }
175
176 content.setLength(0);
177
178 getNodeValue(name, location, param, content);
179 String value = subs.replace(content);
180 if (LOG.isDebugEnabled()) {
181 LOG.debug(prefix + INDENT + "* " + name + "=[" + ConfigLogger.getDisplaySafeValue(name, value) + "]");
182 }
183
184 if (IMPORT_NAME.equals(name)) {
185
186
187
188 if (!value.contains(ALTERNATE_BUILD_LOCATION_KEY)) {
189 parse(params, value, subs, depth + 1);
190 }
191 } else {
192 if (Boolean.valueOf(param.getAttribute(RANDOM_ATTR))) {
193
194 value = String.valueOf(generateRandomInteger(value));
195 }
196 setParam(params, override, name, value, prefix + INDENT);
197 }
198 }
199 LOG.info(prefix + "- Parsed config: " + location);
200 }
201
202
203
204
205
206
207 protected int generateRandomInteger(String rangeSpec) {
208 String[] range = rangeSpec.split("-");
209 if (range.length != 2) {
210 throw new RuntimeException("Invalid range specifier: " + rangeSpec);
211 }
212 int from = Integer.parseInt(range[0].trim());
213 int to = Integer.parseInt(range[1].trim());
214 if (from > to) {
215 int tmp = from;
216 from = to;
217 to = tmp;
218 }
219 int num;
220
221 if (from == to) {
222 num = from;
223 } else {
224 num = from + RANDOM.nextInt((to - from) + 1);
225 }
226 return num;
227 }
228
229
230
231
232
233
234
235
236 protected void getNodeValue(String name, String location, Node n, StringBuilder sb) throws IOException {
237 NodeList children = n.getChildNodes();
238
239 try {
240 sb.setLength(0);
241 for (int j = 0; j < children.getLength(); j++) {
242 sb.append(XmlJotter.writeNode(children.item(j), true));
243 }
244 } catch (TransformerException te) {
245 IOException ioe = new IOException("Error obtaining parameter '" + name + "' from config resource: " + location);
246 ioe.initCause(te);
247 throw ioe;
248 }
249 }
250
251
252
253
254
255
256
257
258
259 private void setParam(Map params, Boolean override, String name, String value, String indent) {
260 if (value == null || "null".equals(value)) {
261 LOG.warn("Not adding property [" + name + "] because it is null - most likely no token could be found for substituion.");
262 return;
263 }
264 if (override) {
265 final String message;
266 Object existingValue = params.get(name);
267 if (existingValue != null) {
268
269 message = indent + "Overriding property " + name + "=[" + existingValue + "] with " + name + "=[" + value + "]";
270
271 params.remove(name);
272 } else {
273 message = indent + "Defining property " + name + "=[" + value + "]";
274 }
275 LOG.debug(message);
276 params.put(name, value);
277 } else if (!params.containsKey(name)) {
278 LOG.debug(indent + "Defining property " + name + "=[" + value + "]");
279 params.put(name, value);
280 } else {
281 LOG.debug(indent + "Not overriding existing parameter: " + name + " '" + params.get(name) + "'");
282 }
283 }
284 }