Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ConfigurationFactory |
|
| 1.5348837209302326;1,535 | ||||
ConfigurationFactory$AdditionalConfigurationData |
|
| 1.5348837209302326;1,535 | ||||
ConfigurationFactory$CallOptionalMethodRule |
|
| 1.5348837209302326;1,535 | ||||
ConfigurationFactory$ConfigurationBuilder |
|
| 1.5348837209302326;1,535 | ||||
ConfigurationFactory$DigesterConfigurationFactory |
|
| 1.5348837209302326;1,535 | ||||
ConfigurationFactory$FileConfigurationFactory |
|
| 1.5348837209302326;1,535 | ||||
ConfigurationFactory$JNDIConfigurationFactory |
|
| 1.5348837209302326;1,535 | ||||
ConfigurationFactory$PropertiesConfigurationFactory |
|
| 1.5348837209302326;1,535 | ||||
ConfigurationFactory$PropertyListConfigurationFactory |
|
| 1.5348837209302326;1,535 | ||||
ConfigurationFactory$SystemConfigurationFactory |
|
| 1.5348837209302326;1,535 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
3 | * contributor license agreements. See the NOTICE file distributed with | |
4 | * this work for additional information regarding copyright ownership. | |
5 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
6 | * (the "License"); you may not use this file except in compliance with | |
7 | * the License. You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | ||
18 | package org.apache.commons.configuration; | |
19 | ||
20 | import java.io.File; | |
21 | import java.io.IOException; | |
22 | import java.io.InputStream; | |
23 | import java.net.URL; | |
24 | import java.util.Collection; | |
25 | import java.util.Iterator; | |
26 | import java.util.LinkedList; | |
27 | import java.util.Map; | |
28 | ||
29 | import org.apache.commons.configuration.plist.PropertyListConfiguration; | |
30 | import org.apache.commons.configuration.plist.XMLPropertyListConfiguration; | |
31 | import org.apache.commons.digester.AbstractObjectCreationFactory; | |
32 | import org.apache.commons.digester.CallMethodRule; | |
33 | import org.apache.commons.digester.Digester; | |
34 | import org.apache.commons.digester.ObjectCreationFactory; | |
35 | import org.apache.commons.digester.Substitutor; | |
36 | import org.apache.commons.digester.substitution.MultiVariableExpander; | |
37 | import org.apache.commons.digester.substitution.VariableSubstitutor; | |
38 | import org.apache.commons.digester.xmlrules.DigesterLoader; | |
39 | import org.apache.commons.lang.StringUtils; | |
40 | import org.apache.commons.logging.Log; | |
41 | import org.apache.commons.logging.LogFactory; | |
42 | import org.xml.sax.Attributes; | |
43 | import org.xml.sax.SAXException; | |
44 | ||
45 | /** | |
46 | * <p> | |
47 | * Factory class to create a CompositeConfiguration from a .xml file using | |
48 | * Digester. By default it can handle the Configurations from commons- | |
49 | * configuration. If you need to add your own, then you can pass in your own | |
50 | * digester rules to use. It is also namespace aware, by providing a | |
51 | * digesterRuleNamespaceURI. | |
52 | * </p> | |
53 | * <p> | |
54 | * <em>Note:</em> Almost all of the features provided by this class and many | |
55 | * more are also available for the <code>{@link DefaultConfigurationBuilder}</code> | |
56 | * class. <code>DefaultConfigurationBuilder</code> also has a more robust | |
57 | * merge algorithm for constructing combined configurations. So it is | |
58 | * recommended to use this class instead of <code>ConfigurationFactory</code>. | |
59 | * </p> | |
60 | * | |
61 | * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a> | |
62 | * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a> | |
63 | * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a> | |
64 | * @version $Id: ConfigurationFactory.java 524006 2007-03-30 09:33:17Z oheger $ | |
65 | */ | |
66 | 32 | public class ConfigurationFactory |
67 | { | |
68 | /** Constant for the root element in the info file.*/ | |
69 | private static final String SEC_ROOT = "configuration/"; | |
70 | ||
71 | /** Constant for the override section.*/ | |
72 | private static final String SEC_OVERRIDE = SEC_ROOT + "override/"; | |
73 | ||
74 | /** Constant for the additional section.*/ | |
75 | private static final String SEC_ADDITIONAL = SEC_ROOT + "additional/"; | |
76 | ||
77 | /** Constant for the optional attribute.*/ | |
78 | private static final String ATTR_OPTIONAL = "optional"; | |
79 | ||
80 | /** Constant for the fileName attribute.*/ | |
81 | private static final String ATTR_FILENAME = "fileName"; | |
82 | ||
83 | /** Constant for the load method.*/ | |
84 | private static final String METH_LOAD = "load"; | |
85 | ||
86 | /** Constant for the default base path (points to actual directory).*/ | |
87 | private static final String DEF_BASE_PATH = "."; | |
88 | ||
89 | /** static logger */ | |
90 | 6 | private static Log log = LogFactory.getLog(ConfigurationFactory.class); |
91 | ||
92 | /** The XML file with the details about the configuration to load */ | |
93 | private String configurationFileName; | |
94 | ||
95 | /** The URL to the XML file with the details about the configuration to load. */ | |
96 | private URL configurationURL; | |
97 | ||
98 | /** | |
99 | * The implicit base path for included files. This path is determined by | |
100 | * the configuration to load and used unless no other base path was | |
101 | * explicitely specified. | |
102 | */ | |
103 | private String implicitBasePath; | |
104 | ||
105 | /** The basePath to prefix file paths for file based property files. */ | |
106 | private String basePath; | |
107 | ||
108 | /** URL for xml digester rules file */ | |
109 | private URL digesterRules; | |
110 | ||
111 | /** The digester namespace to parse */ | |
112 | private String digesterRuleNamespaceURI; | |
113 | ||
114 | /** | |
115 | * Constructor | |
116 | */ | |
117 | public ConfigurationFactory() | |
118 | 30 | { |
119 | 30 | setBasePath(DEF_BASE_PATH); |
120 | 30 | } |
121 | /** | |
122 | * Constructor with ConfigurationFile Name passed | |
123 | * | |
124 | * @param configurationFileName The path to the configuration file | |
125 | */ | |
126 | public ConfigurationFactory(String configurationFileName) | |
127 | 2 | { |
128 | 2 | setConfigurationFileName(configurationFileName); |
129 | 2 | } |
130 | ||
131 | /** | |
132 | * Return the configuration provided by this factory. It loads the | |
133 | * configuration file which is a XML description of the actual | |
134 | * configurations to load. It can contain various different types of | |
135 | * configuration, e.g. Properties, XML and JNDI. | |
136 | * | |
137 | * @return A Configuration object | |
138 | * @throws ConfigurationException A generic exception that we had trouble during the | |
139 | * loading of the configuration data. | |
140 | */ | |
141 | public Configuration getConfiguration() throws ConfigurationException | |
142 | { | |
143 | Digester digester; | |
144 | 29 | InputStream input = null; |
145 | 29 | ConfigurationBuilder builder = new ConfigurationBuilder(); |
146 | 29 | URL url = getConfigurationURL(); |
147 | try | |
148 | { | |
149 | 29 | if (url == null) |
150 | { | |
151 | 22 | url = ConfigurationUtils.locate(implicitBasePath, getConfigurationFileName()); |
152 | } | |
153 | 29 | input = url.openStream(); |
154 | 28 | } |
155 | catch (Exception e) | |
156 | { | |
157 | 1 | log.error("Exception caught opening stream to URL", e); |
158 | 1 | throw new ConfigurationException("Exception caught opening stream to URL", e); |
159 | } | |
160 | ||
161 | 28 | if (getDigesterRules() == null) |
162 | { | |
163 | 27 | digester = new Digester(); |
164 | 27 | configureNamespace(digester); |
165 | 27 | initDefaultDigesterRules(digester); |
166 | } | |
167 | else | |
168 | { | |
169 | 1 | digester = DigesterLoader.createDigester(getDigesterRules()); |
170 | // This might already be too late. As far as I can see, the namespace | |
171 | // awareness must be configured before the digester rules are loaded. | |
172 | 1 | configureNamespace(digester); |
173 | } | |
174 | ||
175 | // Configure digester to always enable the context class loader | |
176 | 28 | digester.setUseContextClassLoader(true); |
177 | // Add a substitutor to resolve system properties | |
178 | 28 | enableDigesterSubstitutor(digester); |
179 | // Put the composite builder object below all of the other objects. | |
180 | 28 | digester.push(builder); |
181 | // Parse the input stream to configure our mappings | |
182 | try | |
183 | { | |
184 | 28 | digester.parse(input); |
185 | 26 | input.close(); |
186 | 26 | } |
187 | catch (SAXException saxe) | |
188 | { | |
189 | 2 | log.error("SAX Exception caught", saxe); |
190 | 2 | throw new ConfigurationException("SAX Exception caught", saxe); |
191 | } | |
192 | catch (IOException ioe) | |
193 | { | |
194 | 0 | log.error("IO Exception caught", ioe); |
195 | 0 | throw new ConfigurationException("IO Exception caught", ioe); |
196 | } | |
197 | 26 | return builder.getConfiguration(); |
198 | } | |
199 | ||
200 | /** | |
201 | * Returns the configurationFile. | |
202 | * | |
203 | * @return The name of the configuration file. Can be null. | |
204 | */ | |
205 | public String getConfigurationFileName() | |
206 | { | |
207 | 22 | return configurationFileName; |
208 | } | |
209 | ||
210 | /** | |
211 | * Sets the configurationFile. | |
212 | * | |
213 | * @param configurationFileName The name of the configurationFile to use. | |
214 | */ | |
215 | public void setConfigurationFileName(String configurationFileName) | |
216 | { | |
217 | 25 | File file = new File(configurationFileName).getAbsoluteFile(); |
218 | 25 | this.configurationFileName = file.getName(); |
219 | 25 | implicitBasePath = file.getParent(); |
220 | 25 | } |
221 | ||
222 | /** | |
223 | * Returns the URL of the configuration file to be loaded. | |
224 | * | |
225 | * @return the URL of the configuration to load | |
226 | */ | |
227 | public URL getConfigurationURL() | |
228 | { | |
229 | 29 | return configurationURL; |
230 | } | |
231 | ||
232 | /** | |
233 | * Sets the URL of the configuration to load. This configuration can be | |
234 | * either specified by a file name or by a URL. | |
235 | * | |
236 | * @param url the URL of the configuration to load | |
237 | */ | |
238 | public void setConfigurationURL(URL url) | |
239 | { | |
240 | 8 | configurationURL = url; |
241 | 8 | implicitBasePath = url.toString(); |
242 | 8 | } |
243 | ||
244 | /** | |
245 | * Returns the digesterRules. | |
246 | * | |
247 | * @return URL | |
248 | */ | |
249 | public URL getDigesterRules() | |
250 | { | |
251 | 29 | return digesterRules; |
252 | } | |
253 | ||
254 | /** | |
255 | * Sets the digesterRules. | |
256 | * | |
257 | * @param digesterRules The digesterRules to set | |
258 | */ | |
259 | public void setDigesterRules(URL digesterRules) | |
260 | { | |
261 | 1 | this.digesterRules = digesterRules; |
262 | 1 | } |
263 | ||
264 | /** | |
265 | * Adds a substitutor to interpolate system properties | |
266 | * | |
267 | * @param digester The digester to which we add the substitutor | |
268 | */ | |
269 | protected void enableDigesterSubstitutor(Digester digester) | |
270 | { | |
271 | 28 | Map systemProperties = System.getProperties(); |
272 | 28 | MultiVariableExpander expander = new MultiVariableExpander(); |
273 | 28 | expander.addSource("$", systemProperties); |
274 | ||
275 | // allow expansion in both xml attributes and element text | |
276 | 28 | Substitutor substitutor = new VariableSubstitutor(expander); |
277 | 28 | digester.setSubstitutor(substitutor); |
278 | 28 | } |
279 | ||
280 | /** | |
281 | * Initializes the parsing rules for the default digester | |
282 | * | |
283 | * This allows the Configuration Factory to understand the default types: | |
284 | * Properties, XML and JNDI. Two special sections are introduced: | |
285 | * <code><override></code> and <code><additional></code>. | |
286 | * | |
287 | * @param digester The digester to configure | |
288 | */ | |
289 | protected void initDefaultDigesterRules(Digester digester) | |
290 | { | |
291 | 27 | initDigesterSectionRules(digester, SEC_ROOT, false); |
292 | 27 | initDigesterSectionRules(digester, SEC_OVERRIDE, false); |
293 | 27 | initDigesterSectionRules(digester, SEC_ADDITIONAL, true); |
294 | 27 | } |
295 | ||
296 | /** | |
297 | * Sets up digester rules for a specified section of the configuration | |
298 | * info file. | |
299 | * | |
300 | * @param digester the current digester instance | |
301 | * @param matchString specifies the section | |
302 | * @param additional a flag if rules for the additional section are to be | |
303 | * added | |
304 | */ | |
305 | protected void initDigesterSectionRules(Digester digester, String matchString, boolean additional) | |
306 | { | |
307 | 81 | setupDigesterInstance( |
308 | digester, | |
309 | matchString + "properties", | |
310 | new PropertiesConfigurationFactory(), | |
311 | METH_LOAD, | |
312 | additional); | |
313 | ||
314 | 81 | setupDigesterInstance( |
315 | digester, | |
316 | matchString + "plist", | |
317 | new PropertyListConfigurationFactory(), | |
318 | METH_LOAD, | |
319 | additional); | |
320 | ||
321 | 81 | setupDigesterInstance( |
322 | digester, | |
323 | matchString + "xml", | |
324 | new FileConfigurationFactory(XMLConfiguration.class), | |
325 | METH_LOAD, | |
326 | additional); | |
327 | ||
328 | 81 | setupDigesterInstance( |
329 | digester, | |
330 | matchString + "hierarchicalXml", | |
331 | new FileConfigurationFactory(XMLConfiguration.class), | |
332 | METH_LOAD, | |
333 | additional); | |
334 | ||
335 | 81 | setupDigesterInstance( |
336 | digester, | |
337 | matchString + "jndi", | |
338 | new JNDIConfigurationFactory(), | |
339 | null, | |
340 | additional); | |
341 | ||
342 | 81 | setupDigesterInstance( |
343 | digester, | |
344 | matchString + "system", | |
345 | new SystemConfigurationFactory(), | |
346 | null, | |
347 | additional); | |
348 | 81 | } |
349 | ||
350 | /** | |
351 | * Sets up digester rules for a configuration to be loaded. | |
352 | * | |
353 | * @param digester the current digester | |
354 | * @param matchString the pattern to match with this rule | |
355 | * @param factory an ObjectCreationFactory instance to use for creating new | |
356 | * objects | |
357 | * @param method the name of a method to be called or <b>null</b> for none | |
358 | * @param additional a flag if rules for the additional section are to be | |
359 | * added | |
360 | */ | |
361 | protected void setupDigesterInstance( | |
362 | Digester digester, | |
363 | String matchString, | |
364 | ObjectCreationFactory factory, | |
365 | String method, | |
366 | boolean additional) | |
367 | { | |
368 | 486 | if (additional) |
369 | { | |
370 | 162 | setupUnionRules(digester, matchString); |
371 | } | |
372 | ||
373 | 486 | digester.addFactoryCreate(matchString, factory); |
374 | 486 | digester.addSetProperties(matchString); |
375 | ||
376 | 486 | if (method != null) |
377 | { | |
378 | 324 | digester.addRule(matchString, new CallOptionalMethodRule(method)); |
379 | } | |
380 | ||
381 | 486 | digester.addSetNext(matchString, "addConfiguration", Configuration.class.getName()); |
382 | 486 | } |
383 | ||
384 | /** | |
385 | * Sets up rules for configurations in the additional section. | |
386 | * | |
387 | * @param digester the current digester | |
388 | * @param matchString the pattern to match with this rule | |
389 | */ | |
390 | protected void setupUnionRules(Digester digester, String matchString) | |
391 | { | |
392 | 162 | digester.addObjectCreate(matchString, |
393 | AdditionalConfigurationData.class); | |
394 | 162 | digester.addSetProperties(matchString); |
395 | 162 | digester.addSetNext(matchString, "addAdditionalConfig", |
396 | AdditionalConfigurationData.class.getName()); | |
397 | 162 | } |
398 | ||
399 | /** | |
400 | * Returns the digesterRuleNamespaceURI. | |
401 | * | |
402 | * @return A String with the digesterRuleNamespaceURI. | |
403 | */ | |
404 | public String getDigesterRuleNamespaceURI() | |
405 | { | |
406 | 29 | return digesterRuleNamespaceURI; |
407 | } | |
408 | ||
409 | /** | |
410 | * Sets the digesterRuleNamespaceURI. | |
411 | * | |
412 | * @param digesterRuleNamespaceURI The new digesterRuleNamespaceURI to use | |
413 | */ | |
414 | public void setDigesterRuleNamespaceURI(String digesterRuleNamespaceURI) | |
415 | { | |
416 | 1 | this.digesterRuleNamespaceURI = digesterRuleNamespaceURI; |
417 | 1 | } |
418 | ||
419 | /** | |
420 | * Configure the current digester to be namespace aware and to have | |
421 | * a Configuration object to which all of the other configurations | |
422 | * should be added | |
423 | * | |
424 | * @param digester The Digester to configure | |
425 | */ | |
426 | private void configureNamespace(Digester digester) | |
427 | { | |
428 | 28 | if (getDigesterRuleNamespaceURI() != null) |
429 | { | |
430 | 1 | digester.setNamespaceAware(true); |
431 | 1 | digester.setRuleNamespaceURI(getDigesterRuleNamespaceURI()); |
432 | } | |
433 | else | |
434 | { | |
435 | 27 | digester.setNamespaceAware(false); |
436 | } | |
437 | 28 | digester.setValidating(false); |
438 | 28 | } |
439 | ||
440 | /** | |
441 | * Returns the Base path from which this Configuration Factory operates. | |
442 | * This is never null. If you set the BasePath to null, then a base path | |
443 | * according to the configuration to load is returned. | |
444 | * | |
445 | * @return The base Path of this configuration factory. | |
446 | */ | |
447 | public String getBasePath() | |
448 | { | |
449 | 61 | String path = StringUtils.isEmpty(basePath) |
450 | || DEF_BASE_PATH.equals(basePath) ? implicitBasePath : basePath; | |
451 | 61 | return StringUtils.isEmpty(path) ? DEF_BASE_PATH : path; |
452 | } | |
453 | ||
454 | /** | |
455 | * Sets the basePath for all file references from this Configuration Factory. | |
456 | * Normally a base path need not to be set because it is determined by | |
457 | * the location of the configuration file to load. All relative pathes in | |
458 | * this file are resolved relative to this file. Setting a base path makes | |
459 | * sense if such relative pathes should be otherwise resolved, e.g. if | |
460 | * the configuration file is loaded from the class path and all sub | |
461 | * configurations it refers to are stored in a special config directory. | |
462 | * | |
463 | * @param basePath The new basePath to set. | |
464 | */ | |
465 | public void setBasePath(String basePath) | |
466 | { | |
467 | 34 | this.basePath = basePath; |
468 | 34 | } |
469 | ||
470 | /** | |
471 | * A base class for digester factory classes. This base class maintains | |
472 | * a default class for the objects to be created. | |
473 | * There will be sub classes for specific configuration implementations. | |
474 | */ | |
475 | public class DigesterConfigurationFactory extends AbstractObjectCreationFactory | |
476 | { | |
477 | /** Actual class to use. */ | |
478 | private Class clazz; | |
479 | ||
480 | /** | |
481 | * Creates a new instance of <code>DigesterConfigurationFactory</code>. | |
482 | * | |
483 | * @param clazz the class which we should instantiate | |
484 | */ | |
485 | public DigesterConfigurationFactory(Class clazz) | |
486 | 486 | { |
487 | 486 | this.clazz = clazz; |
488 | 486 | } |
489 | ||
490 | /** | |
491 | * Creates an instance of the specified class. | |
492 | * | |
493 | * @param attribs the attributes (ignored) | |
494 | * @return the new object | |
495 | * @throws Exception if object creation fails | |
496 | */ | |
497 | public Object createObject(Attributes attribs) throws Exception | |
498 | { | |
499 | 21 | return clazz.newInstance(); |
500 | } | |
501 | } | |
502 | ||
503 | /** | |
504 | * A tiny inner class that allows the Configuration Factory to | |
505 | * let the digester construct FileConfiguration objects | |
506 | * that already have the correct base Path set. | |
507 | * | |
508 | */ | |
509 | public class FileConfigurationFactory extends DigesterConfigurationFactory | |
510 | { | |
511 | /** | |
512 | * C'tor | |
513 | * | |
514 | * @param clazz The class which we should instantiate. | |
515 | */ | |
516 | public FileConfigurationFactory(Class clazz) | |
517 | 324 | { |
518 | 324 | super(clazz); |
519 | 324 | } |
520 | ||
521 | /** | |
522 | * Gets called by the digester. | |
523 | * | |
524 | * @param attributes the actual attributes | |
525 | * @return the new object | |
526 | * @throws Exception Couldn't instantiate the requested object. | |
527 | */ | |
528 | public Object createObject(Attributes attributes) throws Exception | |
529 | { | |
530 | 55 | FileConfiguration conf = createConfiguration(attributes); |
531 | 55 | conf.setBasePath(getBasePath()); |
532 | 55 | return conf; |
533 | } | |
534 | ||
535 | /** | |
536 | * Creates the object, a <code>FileConfiguration</code>. | |
537 | * | |
538 | * @param attributes the actual attributes | |
539 | * @return the file configuration | |
540 | * @throws Exception if the object could not be created | |
541 | */ | |
542 | protected FileConfiguration createConfiguration(Attributes attributes) throws Exception | |
543 | { | |
544 | 19 | return (FileConfiguration) super.createObject(attributes); |
545 | } | |
546 | } | |
547 | ||
548 | /** | |
549 | * A factory that returns an XMLPropertiesConfiguration for .xml files | |
550 | * and a PropertiesConfiguration for the others. | |
551 | * | |
552 | * @since 1.2 | |
553 | */ | |
554 | public class PropertiesConfigurationFactory extends FileConfigurationFactory | |
555 | { | |
556 | /** | |
557 | * Creates a new instance of <code>PropertiesConfigurationFactory</code>. | |
558 | */ | |
559 | public PropertiesConfigurationFactory() | |
560 | 81 | { |
561 | 81 | super(null); |
562 | 81 | } |
563 | ||
564 | /** | |
565 | * Creates the new configuration object. Based on the file name | |
566 | * provided in the attributes either a <code>PropertiesConfiguration</code> | |
567 | * or a <code>XMLPropertiesConfiguration</code> object will be | |
568 | * returned. | |
569 | * | |
570 | * @param attributes the attributes | |
571 | * @return the new configuration object | |
572 | * @throws Exception if an error occurs | |
573 | */ | |
574 | protected FileConfiguration createConfiguration(Attributes attributes) throws Exception | |
575 | { | |
576 | 36 | String filename = attributes.getValue(ATTR_FILENAME); |
577 | ||
578 | 36 | if (filename != null && filename.toLowerCase().trim().endsWith(".xml")) |
579 | { | |
580 | 2 | return new XMLPropertiesConfiguration(); |
581 | } | |
582 | else | |
583 | { | |
584 | 34 | return new PropertiesConfiguration(); |
585 | } | |
586 | } | |
587 | } | |
588 | ||
589 | /** | |
590 | * A factory that returns an XMLPropertyListConfiguration for .xml files | |
591 | * and a PropertyListConfiguration for the others. | |
592 | * | |
593 | * @since 1.2 | |
594 | */ | |
595 | public class PropertyListConfigurationFactory extends FileConfigurationFactory | |
596 | { | |
597 | /** | |
598 | * Creates a new instance of <code>PropertyListConfigurationFactory</code>. | |
599 | */ | |
600 | public PropertyListConfigurationFactory() | |
601 | 81 | { |
602 | 81 | super(null); |
603 | 81 | } |
604 | ||
605 | /** | |
606 | * Creates the new configuration object. Based on the file name | |
607 | * provided in the attributes either a <code>XMLPropertyListConfiguration</code> | |
608 | * or a <code>PropertyListConfiguration</code> object will be | |
609 | * returned. | |
610 | * | |
611 | * @param attributes the attributes | |
612 | * @return the new configuration object | |
613 | * @throws Exception if an error occurs | |
614 | */ | |
615 | protected FileConfiguration createConfiguration(Attributes attributes) throws Exception | |
616 | { | |
617 | 0 | String filename = attributes.getValue(ATTR_FILENAME); |
618 | ||
619 | 0 | if (filename != null && filename.toLowerCase().trim().endsWith(".xml")) |
620 | { | |
621 | 0 | return new XMLPropertyListConfiguration(); |
622 | } | |
623 | else | |
624 | { | |
625 | 0 | return new PropertyListConfiguration(); |
626 | } | |
627 | } | |
628 | } | |
629 | ||
630 | /** | |
631 | * A tiny inner class that allows the Configuration Factory to | |
632 | * let the digester construct JNDIConfiguration objects. | |
633 | */ | |
634 | private class JNDIConfigurationFactory extends DigesterConfigurationFactory | |
635 | { | |
636 | /** | |
637 | * Creates a new instance of <code>JNDIConfigurationFactory</code>. | |
638 | */ | |
639 | 6 | public JNDIConfigurationFactory() |
640 | 81 | { |
641 | 81 | super(JNDIConfiguration.class); |
642 | 81 | } |
643 | } | |
644 | ||
645 | /** | |
646 | * A tiny inner class that allows the Configuration Factory to | |
647 | * let the digester construct SystemConfiguration objects. | |
648 | */ | |
649 | private class SystemConfigurationFactory extends DigesterConfigurationFactory | |
650 | { | |
651 | /** | |
652 | * Creates a new instance of <code>SystemConfigurationFactory</code>. | |
653 | */ | |
654 | 6 | public SystemConfigurationFactory() |
655 | 81 | { |
656 | 81 | super(SystemConfiguration.class); |
657 | 81 | } |
658 | } | |
659 | ||
660 | /** | |
661 | * A simple data class that holds all information about a configuration | |
662 | * from the <code><additional></code> section. | |
663 | */ | |
664 | 21 | public static class AdditionalConfigurationData |
665 | { | |
666 | /** Stores the configuration object.*/ | |
667 | private Configuration configuration; | |
668 | ||
669 | /** Stores the location of this configuration in the global tree.*/ | |
670 | private String at; | |
671 | ||
672 | /** | |
673 | * Returns the value of the <code>at</code> attribute. | |
674 | * | |
675 | * @return the at attribute | |
676 | */ | |
677 | public String getAt() | |
678 | { | |
679 | 20 | return at; |
680 | } | |
681 | ||
682 | /** | |
683 | * Sets the value of the <code>at</code> attribute. | |
684 | * | |
685 | * @param string the attribute value | |
686 | */ | |
687 | public void setAt(String string) | |
688 | { | |
689 | 10 | at = string; |
690 | 10 | } |
691 | ||
692 | /** | |
693 | * Returns the configuration object. | |
694 | * | |
695 | * @return the configuration | |
696 | */ | |
697 | public Configuration getConfiguration() | |
698 | { | |
699 | 40 | return configuration; |
700 | } | |
701 | ||
702 | /** | |
703 | * Sets the configuration object. Note: Normally this method should be | |
704 | * named <code>setConfiguration()</code>, but the name | |
705 | * <code>addConfiguration()</code> is required by some of the digester | |
706 | * rules. | |
707 | * | |
708 | * @param config the configuration to set | |
709 | */ | |
710 | public void addConfiguration(Configuration config) | |
711 | { | |
712 | 21 | configuration = config; |
713 | 21 | } |
714 | } | |
715 | ||
716 | /** | |
717 | * An internally used helper class for constructing the composite | |
718 | * configuration object. | |
719 | */ | |
720 | public static class ConfigurationBuilder | |
721 | { | |
722 | /** Stores the composite configuration.*/ | |
723 | private CompositeConfiguration config; | |
724 | ||
725 | /** Stores a collection with the configs from the additional section.*/ | |
726 | private Collection additionalConfigs; | |
727 | ||
728 | /** | |
729 | * Creates a new instance of <code>ConfigurationBuilder</code>. | |
730 | */ | |
731 | public ConfigurationBuilder() | |
732 | 29 | { |
733 | 29 | config = new CompositeConfiguration(); |
734 | 29 | additionalConfigs = new LinkedList(); |
735 | 29 | } |
736 | ||
737 | /** | |
738 | * Adds a new configuration to this object. This method is called by | |
739 | * Digester. | |
740 | * | |
741 | * @param conf the configuration to be added | |
742 | */ | |
743 | public void addConfiguration(Configuration conf) | |
744 | { | |
745 | 44 | config.addConfiguration(conf); |
746 | 44 | } |
747 | ||
748 | /** | |
749 | * Adds information about an additional configuration. This method is | |
750 | * called by Digester. | |
751 | * | |
752 | * @param data the data about the additional configuration | |
753 | */ | |
754 | public void addAdditionalConfig(AdditionalConfigurationData data) | |
755 | { | |
756 | 21 | additionalConfigs.add(data); |
757 | 21 | } |
758 | ||
759 | /** | |
760 | * Returns the final composite configuration. | |
761 | * | |
762 | * @return the final configuration object | |
763 | */ | |
764 | public CompositeConfiguration getConfiguration() | |
765 | { | |
766 | 26 | if (!additionalConfigs.isEmpty()) |
767 | { | |
768 | 6 | Configuration unionConfig = createAdditionalConfiguration(additionalConfigs); |
769 | 6 | if (unionConfig != null) |
770 | { | |
771 | 5 | addConfiguration(unionConfig); |
772 | } | |
773 | 6 | additionalConfigs.clear(); |
774 | } | |
775 | ||
776 | 26 | return config; |
777 | } | |
778 | ||
779 | /** | |
780 | * Creates a configuration object with the union of all properties | |
781 | * defined in the <code><additional></code> section. This | |
782 | * implementation returns a <code>HierarchicalConfiguration</code> | |
783 | * object. | |
784 | * | |
785 | * @param configs a collection with | |
786 | * <code>AdditionalConfigurationData</code> objects | |
787 | * @return the union configuration (can be <b>null</b>) | |
788 | */ | |
789 | protected Configuration createAdditionalConfiguration(Collection configs) | |
790 | { | |
791 | 6 | HierarchicalConfiguration result = new HierarchicalConfiguration(); |
792 | ||
793 | 32 | for (Iterator it = configs.iterator(); it.hasNext();) |
794 | { | |
795 | 20 | AdditionalConfigurationData cdata = |
796 | (AdditionalConfigurationData) it.next(); | |
797 | 20 | result.addNodes(cdata.getAt(), |
798 | createRootNode(cdata).getChildren()); | |
799 | } | |
800 | ||
801 | 6 | return result.isEmpty() ? null : result; |
802 | } | |
803 | ||
804 | /** | |
805 | * Creates a configuration root node for the specified configuration. | |
806 | * | |
807 | * @param cdata the configuration data object | |
808 | * @return a root node for this configuration | |
809 | */ | |
810 | private HierarchicalConfiguration.Node createRootNode(AdditionalConfigurationData cdata) | |
811 | { | |
812 | 20 | if (cdata.getConfiguration() instanceof HierarchicalConfiguration) |
813 | { | |
814 | // we can directly use this configuration's root node | |
815 | 12 | return ((HierarchicalConfiguration) cdata.getConfiguration()).getRoot(); |
816 | } | |
817 | else | |
818 | { | |
819 | // transform configuration to a hierarchical root node | |
820 | 8 | HierarchicalConfiguration hc = new HierarchicalConfiguration(); |
821 | 8 | ConfigurationUtils.copy(cdata.getConfiguration(), hc); |
822 | 8 | return hc.getRoot(); |
823 | } | |
824 | } | |
825 | } | |
826 | ||
827 | /** | |
828 | * A special implementation of Digester's <code>CallMethodRule</code> that | |
829 | * is internally used for calling a file configuration's <code>load()</code> | |
830 | * method. This class difers from its ancestor that it catches all occuring | |
831 | * exceptions when the specified method is called. It then checks whether | |
832 | * for the corresponding configuration the optional attribute is set. If | |
833 | * this is the case, the exception will simply be ignored. | |
834 | * | |
835 | * @since 1.4 | |
836 | */ | |
837 | private static class CallOptionalMethodRule extends CallMethodRule | |
838 | { | |
839 | /** A flag whether the optional attribute is set for this node. */ | |
840 | private boolean optional; | |
841 | ||
842 | /** | |
843 | * Creates a new instance of <code>CallOptionalMethodRule</code> and | |
844 | * sets the name of the method to invoke. | |
845 | * | |
846 | * @param methodName the name of the method | |
847 | */ | |
848 | public CallOptionalMethodRule(String methodName) | |
849 | { | |
850 | 324 | super(methodName); |
851 | 324 | } |
852 | ||
853 | /** | |
854 | * Checks if the optional attribute is set. | |
855 | * | |
856 | * @param attrs the attributes | |
857 | * @throws Exception if an error occurs | |
858 | */ | |
859 | public void begin(Attributes attrs) throws Exception | |
860 | { | |
861 | 55 | optional = attrs.getValue(ATTR_OPTIONAL) != null |
862 | && PropertyConverter.toBoolean( | |
863 | attrs.getValue(ATTR_OPTIONAL)).booleanValue(); | |
864 | 55 | super.begin(attrs); |
865 | 55 | } |
866 | ||
867 | /** | |
868 | * Calls the method. If the optional attribute was set, occurring | |
869 | * exceptions will be ignored. | |
870 | * | |
871 | * @throws Exception if an error occurs | |
872 | */ | |
873 | public void end() throws Exception | |
874 | { | |
875 | try | |
876 | { | |
877 | 55 | super.end(); |
878 | 46 | } |
879 | catch (Exception ex) | |
880 | { | |
881 | 9 | if (optional) |
882 | { | |
883 | 8 | log.warn("Could not create optional configuration!", ex); |
884 | } | |
885 | else | |
886 | { | |
887 | 1 | throw ex; |
888 | } | |
889 | } | |
890 | 54 | } |
891 | } | |
892 | } |