001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.configuration; 018 019 import java.io.File; 020 import java.io.FileNotFoundException; 021 import java.io.InputStream; 022 import java.io.OutputStream; 023 import java.io.Reader; 024 import java.io.Writer; 025 import java.math.BigDecimal; 026 import java.math.BigInteger; 027 import java.net.MalformedURLException; 028 import java.net.URL; 029 import java.util.Collection; 030 import java.util.HashMap; 031 import java.util.Iterator; 032 import java.util.List; 033 import java.util.Map; 034 import java.util.Properties; 035 036 import org.apache.commons.configuration.event.ConfigurationErrorEvent; 037 import org.apache.commons.configuration.event.ConfigurationErrorListener; 038 import org.apache.commons.configuration.event.ConfigurationEvent; 039 import org.apache.commons.configuration.event.ConfigurationListener; 040 import org.apache.commons.configuration.tree.ConfigurationNode; 041 import org.apache.commons.configuration.tree.ExpressionEngine; 042 043 /** 044 * This class provides access to multiple configuration files that reside in a location that 045 * can be specified by a pattern allowing applications to be multi-tenant. For example, 046 * providing a pattern of "file:///opt/config/${product}/${client}/config.xml" will result in 047 * "product" and "client" being resolved on every call. The configuration resulting from the 048 * resolved pattern will be saved for future access. 049 * @since 1.6 050 * @author <a 051 * href="http://commons.apache.org/configuration/team-list.html">Commons 052 * Configuration team</a> 053 * @version $Id: MultiFileHierarchicalConfiguration.java 727958 2008-12-19 07:19:24Z oheger $ 054 */ 055 public class MultiFileHierarchicalConfiguration extends AbstractHierarchicalFileConfiguration 056 implements ConfigurationListener, ConfigurationErrorListener 057 { 058 /** FILE URL prefix */ 059 private static final String FILE_URL_PREFIX = "file:"; 060 061 /** 062 * Prevent recursion while resolving unprefixed properties. 063 */ 064 private static ThreadLocal recursive = new ThreadLocal() 065 { 066 protected synchronized Object initialValue() 067 { 068 return Boolean.FALSE; 069 } 070 }; 071 072 /** Map of configurations */ 073 private final Map configurationsMap = new HashMap(); 074 075 /** key pattern for configurationsMap */ 076 private String pattern; 077 078 /** True if the constructor has finished */ 079 private boolean init; 080 081 /** 082 * Default Constructor. 083 */ 084 public MultiFileHierarchicalConfiguration() 085 { 086 super(); 087 this.init = true; 088 } 089 090 /** 091 * Construct the configuration with the specified pattern. 092 * @param pathPattern The pattern to use to locate configuration files. 093 */ 094 public MultiFileHierarchicalConfiguration(String pathPattern) 095 { 096 super(); 097 this.pattern = pathPattern; 098 this.init = true; 099 } 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 * Don't allow resolveContainerStore to be called recursively. 555 * @param key The key to resolve. 556 * @return The value of the key. 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 // try URL 665 return new URL(resourceLocation); 666 } 667 catch (MalformedURLException ex) 668 { 669 // no URL -> treat as file path 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 }