1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.configuration;
18
19 import java.io.File;
20 import java.io.FileNotFoundException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.io.Reader;
24 import java.io.Writer;
25 import java.math.BigDecimal;
26 import java.math.BigInteger;
27 import java.net.MalformedURLException;
28 import java.net.URL;
29 import java.util.Collection;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Properties;
35
36 import org.apache.commons.configuration.event.ConfigurationErrorEvent;
37 import org.apache.commons.configuration.event.ConfigurationErrorListener;
38 import org.apache.commons.configuration.event.ConfigurationEvent;
39 import org.apache.commons.configuration.event.ConfigurationListener;
40 import org.apache.commons.configuration.tree.ConfigurationNode;
41 import org.apache.commons.configuration.tree.ExpressionEngine;
42
43 /***
44 * This class provides access to multiple configuration files that reside in a location that
45 * can be specified by a pattern allowing applications to be multi-tenant. For example,
46 * providing a pattern of "file:///opt/config/${product}/${client}/config.xml" will result in
47 * "product" and "client" being resolved on every call. The configuration resulting from the
48 * resolved pattern will be saved for future access.
49 * @since 1.6
50 * @author <a
51 * href="http://commons.apache.org/configuration/team-list.html">Commons
52 * Configuration team</a>
53 * @version $Id: MultiFileHierarchicalConfiguration.java 727958 2008-12-19 07:19:24Z oheger $
54 */
55 public class MultiFileHierarchicalConfiguration extends AbstractHierarchicalFileConfiguration
56 implements ConfigurationListener, ConfigurationErrorListener
57 {
58 /*** FILE URL prefix */
59 private static final String FILE_URL_PREFIX = "file:";
60
61 /***
62 * Prevent recursion while resolving unprefixed properties.
63 */
64 private static ThreadLocal recursive = new ThreadLocal()
65 {
66 protected synchronized Object initialValue()
67 {
68 return Boolean.FALSE;
69 }
70 };
71
72 /*** Map of configurations */
73 private final Map configurationsMap = new HashMap();
74
75 /*** key pattern for configurationsMap */
76 private String pattern;
77
78 /*** True if the constructor has finished */
79 private boolean init;
80
81 /***
82 * Default Constructor.
83 */
84 public MultiFileHierarchicalConfiguration()
85 {
86 super();
87 this.init = true;
88 }
89
90 /***
91 * Construct the configuration with the specified pattern.
92 * @param pathPattern The pattern to use to locate configuration files.
93 */
94 public MultiFileHierarchicalConfiguration(String pathPattern)
95 {
96 super();
97 this.pattern = pathPattern;
98 this.init = true;
99 }
100
101 /***
102 * Set the File pattern
103 * @param pathPattern The pattern for the path to the configuration.
104 */
105 public void setFilePattern(String pathPattern)
106 {
107 this.pattern = pathPattern;
108 }
109
110 /***
111 * Creates the file configuration delegate, i.e. the object that implements
112 * functionality required by the <code>FileConfiguration</code> interface.
113 * This base implementation will return an instance of the
114 * <code>FileConfigurationDelegate</code> class. Derived classes may
115 * override it to create a different delegate object.
116 *
117 * @return the file configuration delegate
118 */
119 protected FileConfigurationDelegate createDelegate()
120 {
121 return new FileConfigurationDelegate();
122 }
123
124 public void addProperty(String key, Object value)
125 {
126 this.getConfiguration().addProperty(key, value);
127 }
128
129 public void clear()
130 {
131 this.getConfiguration().clear();
132 }
133
134 public void clearProperty(String key)
135 {
136 this.getConfiguration().clearProperty(key);
137 }
138
139 public boolean containsKey(String key)
140 {
141 return this.getConfiguration().containsKey(key);
142 }
143
144 public BigDecimal getBigDecimal(String key, BigDecimal defaultValue)
145 {
146 return this.getConfiguration().getBigDecimal(key, defaultValue);
147 }
148
149 public BigDecimal getBigDecimal(String key)
150 {
151 return this.getConfiguration().getBigDecimal(key);
152 }
153
154 public BigInteger getBigInteger(String key, BigInteger defaultValue)
155 {
156 return this.getConfiguration().getBigInteger(key, defaultValue);
157 }
158
159 public BigInteger getBigInteger(String key)
160 {
161 return this.getConfiguration().getBigInteger(key);
162 }
163
164 public boolean getBoolean(String key, boolean defaultValue)
165 {
166 return this.getConfiguration().getBoolean(key, defaultValue);
167 }
168
169 public Boolean getBoolean(String key, Boolean defaultValue)
170 {
171 return this.getConfiguration().getBoolean(key, defaultValue);
172 }
173
174 public boolean getBoolean(String key)
175 {
176 return this.getConfiguration().getBoolean(key);
177 }
178
179 public byte getByte(String key, byte defaultValue)
180 {
181 return this.getConfiguration().getByte(key, defaultValue);
182 }
183
184 public Byte getByte(String key, Byte defaultValue)
185 {
186 return this.getConfiguration().getByte(key, defaultValue);
187 }
188
189 public byte getByte(String key)
190 {
191 return this.getConfiguration().getByte(key);
192 }
193
194 public double getDouble(String key, double defaultValue)
195 {
196 return this.getConfiguration().getDouble(key, defaultValue);
197 }
198
199 public Double getDouble(String key, Double defaultValue)
200 {
201 return this.getConfiguration().getDouble(key, defaultValue);
202 }
203
204 public double getDouble(String key)
205 {
206 return this.getConfiguration().getDouble(key);
207 }
208
209 public float getFloat(String key, float defaultValue)
210 {
211 return this.getConfiguration().getFloat(key, defaultValue);
212 }
213
214 public Float getFloat(String key, Float defaultValue)
215 {
216 return this.getConfiguration().getFloat(key, defaultValue);
217 }
218
219 public float getFloat(String key)
220 {
221 return this.getConfiguration().getFloat(key);
222 }
223
224 public int getInt(String key, int defaultValue)
225 {
226 return this.getConfiguration().getInt(key, defaultValue);
227 }
228
229 public int getInt(String key)
230 {
231 return this.getConfiguration().getInt(key);
232 }
233
234 public Integer getInteger(String key, Integer defaultValue)
235 {
236 return this.getConfiguration().getInteger(key, defaultValue);
237 }
238
239 public Iterator getKeys()
240 {
241 return this.getConfiguration().getKeys();
242 }
243
244 public Iterator getKeys(String prefix)
245 {
246 return this.getConfiguration().getKeys(prefix);
247 }
248
249 public List getList(String key, List defaultValue)
250 {
251 return this.getConfiguration().getList(key, defaultValue);
252 }
253
254 public List getList(String key)
255 {
256 return this.getConfiguration().getList(key);
257 }
258
259 public long getLong(String key, long defaultValue)
260 {
261 return this.getConfiguration().getLong(key, defaultValue);
262 }
263
264 public Long getLong(String key, Long defaultValue)
265 {
266 return this.getConfiguration().getLong(key, defaultValue);
267 }
268
269 public long getLong(String key)
270 {
271 return this.getConfiguration().getLong(key);
272 }
273
274 public Properties getProperties(String key)
275 {
276 return this.getConfiguration().getProperties(key);
277 }
278
279 public Object getProperty(String key)
280 {
281 return this.getConfiguration().getProperty(key);
282 }
283
284 public short getShort(String key, short defaultValue)
285 {
286 return this.getConfiguration().getShort(key, defaultValue);
287 }
288
289 public Short getShort(String key, Short defaultValue)
290 {
291 return this.getConfiguration().getShort(key, defaultValue);
292 }
293
294 public short getShort(String key)
295 {
296 return this.getConfiguration().getShort(key);
297 }
298
299 public String getString(String key, String defaultValue)
300 {
301 return this.getConfiguration().getString(key, defaultValue);
302 }
303
304 public String getString(String key)
305 {
306 return this.getConfiguration().getString(key);
307 }
308
309 public String[] getStringArray(String key)
310 {
311 return this.getConfiguration().getStringArray(key);
312 }
313
314 public boolean isEmpty()
315 {
316 return this.getConfiguration().isEmpty();
317 }
318
319 public void setProperty(String key, Object value)
320 {
321 if (init)
322 {
323 this.getConfiguration().setProperty(key, value);
324 }
325 }
326
327 public Configuration subset(String prefix)
328 {
329 return this.getConfiguration().subset(prefix);
330 }
331
332 public Node getRoot()
333 {
334 return this.getConfiguration().getRoot();
335 }
336
337 public void setRoot(Node node)
338 {
339 if (init)
340 {
341 this.getConfiguration().setRoot(node);
342 }
343 else
344 {
345 super.setRoot(node);
346 }
347 }
348
349 public ConfigurationNode getRootNode()
350 {
351 return this.getConfiguration().getRootNode();
352 }
353
354 public void setRootNode(ConfigurationNode rootNode)
355 {
356 if (init)
357 {
358 this.getConfiguration().setRootNode(rootNode);
359 }
360 else
361 {
362 super.setRootNode(rootNode);
363 }
364 }
365
366 public ExpressionEngine getExpressionEngine()
367 {
368 return super.getExpressionEngine();
369 }
370
371 public void setExpressionEngine(ExpressionEngine expressionEngine)
372 {
373 super.setExpressionEngine(expressionEngine);
374 }
375
376 public void addNodes(String key, Collection nodes)
377 {
378 this.getConfiguration().addNodes(key, nodes);
379 }
380
381 public SubnodeConfiguration configurationAt(String key, boolean supportUpdates)
382 {
383 return this.getConfiguration().configurationAt(key, supportUpdates);
384 }
385
386 public SubnodeConfiguration configurationAt(String key)
387 {
388 return this.getConfiguration().configurationAt(key);
389 }
390
391 public List configurationsAt(String key)
392 {
393 return this.getConfiguration().configurationsAt(key);
394 }
395
396 public void clearTree(String key)
397 {
398 this.getConfiguration().clearTree(key);
399 }
400
401 public int getMaxIndex(String key)
402 {
403 return this.getConfiguration().getMaxIndex(key);
404 }
405
406 public Configuration interpolatedConfiguration()
407 {
408 return this.getConfiguration().interpolatedConfiguration();
409 }
410
411 public void addConfigurationListener(ConfigurationListener l)
412 {
413 super.addConfigurationListener(l);
414 }
415
416 public boolean removeConfigurationListener(ConfigurationListener l)
417 {
418 return super.removeConfigurationListener(l);
419 }
420
421 public Collection getConfigurationListeners()
422 {
423 return super.getConfigurationListeners();
424 }
425
426 public void clearConfigurationListeners()
427 {
428 super.clearConfigurationListeners();
429 }
430
431 public void addErrorListener(ConfigurationErrorListener l)
432 {
433 super.addErrorListener(l);
434 }
435
436 public boolean removeErrorListener(ConfigurationErrorListener l)
437 {
438 return super.removeErrorListener(l);
439 }
440
441 public void clearErrorListeners()
442 {
443 super.clearErrorListeners();
444 }
445
446 public Collection getErrorListeners()
447 {
448 return super.getErrorListeners();
449 }
450
451 public void save(Writer writer) throws ConfigurationException
452 {
453 if (init)
454 {
455 this.getConfiguration().save(writer);
456 }
457 }
458
459 public void load(Reader reader) throws ConfigurationException
460 {
461 if (init)
462 {
463 this.getConfiguration().load(reader);
464 }
465 }
466
467 public void load() throws ConfigurationException
468 {
469 this.getConfiguration().load();
470 }
471
472 public void load(String fileName) throws ConfigurationException
473 {
474 this.getConfiguration().load(fileName);
475 }
476
477 public void load(File file) throws ConfigurationException
478 {
479 this.getConfiguration().load(file);
480 }
481
482 public void load(URL url) throws ConfigurationException
483 {
484 this.getConfiguration().load(url);
485 }
486
487 public void load(InputStream in) throws ConfigurationException
488 {
489 this.getConfiguration().load(in);
490 }
491
492 public void load(InputStream in, String encoding) throws ConfigurationException
493 {
494 this.getConfiguration().load(in, encoding);
495 }
496
497 public void save() throws ConfigurationException
498 {
499 this.getConfiguration().save();
500 }
501
502 public void save(String fileName) throws ConfigurationException
503 {
504 this.getConfiguration().save(fileName);
505 }
506
507 public void save(File file) throws ConfigurationException
508 {
509 this.getConfiguration().save(file);
510 }
511
512 public void save(URL url) throws ConfigurationException
513 {
514 this.getConfiguration().save(url);
515 }
516
517 public void save(OutputStream out) throws ConfigurationException
518 {
519 this.getConfiguration().save(out);
520 }
521
522 public void save(OutputStream out, String encoding) throws ConfigurationException
523 {
524 this.getConfiguration().save(out, encoding);
525 }
526
527 public void configurationChanged(ConfigurationEvent event)
528 {
529 if (event.getSource() instanceof XMLConfiguration)
530 {
531 Iterator iter = getConfigurationListeners().iterator();
532 while (iter.hasNext())
533 {
534 ConfigurationListener listener = (ConfigurationListener) iter.next();
535 listener.configurationChanged(event);
536 }
537 }
538 }
539
540 public void configurationError(ConfigurationErrorEvent event)
541 {
542 if (event.getSource() instanceof XMLConfiguration)
543 {
544 Iterator iter = getErrorListeners().iterator();
545 while (iter.hasNext())
546 {
547 ConfigurationErrorListener listener = (ConfigurationErrorListener) iter.next();
548 listener.configurationError(event);
549 }
550 }
551 }
552
553
554
555
556
557
558 protected Object resolveContainerStore(String key)
559 {
560 if (((Boolean) recursive.get()).booleanValue())
561 {
562 return null;
563 }
564 recursive.set(Boolean.TRUE);
565 try
566 {
567 return super.resolveContainerStore(key);
568 }
569 finally
570 {
571 recursive.set(Boolean.FALSE);
572 }
573 }
574
575 /***
576 * Remove the current Configuration.
577 */
578 public void removeConfiguration()
579 {
580 String path = getSubstitutor().replace(pattern);
581 synchronized (configurationsMap)
582 {
583 configurationsMap.remove(path);
584 }
585 }
586
587 /***
588 * First checks to see if the cache exists, if it does, get the associated Configuration.
589 * If not it will load a new Configuration and save it in the cache.
590 *
591 * @return the Configuration associated with the current value of the path pattern.
592 */
593 private AbstractHierarchicalFileConfiguration getConfiguration()
594 {
595 if (pattern == null)
596 {
597 throw new ConfigurationRuntimeException("File pattern must be defined");
598 }
599 String path = getSubstitutor().replace(pattern);
600 synchronized (configurationsMap)
601 {
602 if (configurationsMap.containsKey(path))
603 {
604 return (AbstractHierarchicalFileConfiguration) configurationsMap.get(path);
605 }
606 }
607
608 if (path.equals(pattern))
609 {
610 XMLConfiguration configuration = new XMLConfiguration()
611 {
612 public void load() throws ConfigurationException
613 {
614 }
615 public void save() throws ConfigurationException
616 {
617 }
618 };
619 synchronized (configurationsMap)
620 {
621 configurationsMap.put(pattern, configuration);
622 }
623 return configuration;
624 }
625
626 XMLConfiguration configuration = new XMLConfiguration();
627 try
628 {
629 URL url = getURL(path);
630 configuration.setURL(url);
631 configuration.load();
632 configuration.setExpressionEngine(getExpressionEngine());
633 configuration.setReloadingStrategy(getReloadingStrategy());
634 configuration.addConfigurationListener(this);
635 configuration.addErrorListener(this);
636 synchronized (configurationsMap)
637 {
638 if (!configurationsMap.containsKey(path))
639 {
640 configurationsMap.put(path, configuration);
641 }
642 }
643 }
644 catch (ConfigurationException ce)
645 {
646 throw new ConfigurationRuntimeException(ce);
647 }
648 catch (FileNotFoundException fnfe)
649 {
650 throw new ConfigurationRuntimeException(fnfe);
651 }
652
653 return configuration;
654 }
655
656 private URL getURL(String resourceLocation) throws FileNotFoundException
657 {
658 if (resourceLocation == null)
659 {
660 throw new IllegalArgumentException("A path pattern must be configured");
661 }
662 try
663 {
664
665 return new URL(resourceLocation);
666 }
667 catch (MalformedURLException ex)
668 {
669
670 try
671 {
672 return new URL(FILE_URL_PREFIX + resourceLocation);
673 }
674 catch (MalformedURLException ex2)
675 {
676 throw new FileNotFoundException("Resource location [" + resourceLocation
677 + "] is not a URL or a well-formed file path");
678 }
679 }
680 }
681 }