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 */ 017package org.apache.commons.configuration2; 018 019import java.math.BigDecimal; 020import java.math.BigInteger; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028import java.util.Properties; 029import java.util.Set; 030import java.util.concurrent.ConcurrentHashMap; 031import java.util.concurrent.ConcurrentMap; 032 033import org.apache.commons.configuration2.event.Event; 034import org.apache.commons.configuration2.event.EventListener; 035import org.apache.commons.configuration2.event.EventType; 036import org.apache.commons.configuration2.interpol.ConfigurationInterpolator; 037import org.apache.commons.configuration2.interpol.Lookup; 038import org.apache.commons.configuration2.io.ConfigurationLogger; 039import org.apache.commons.configuration2.tree.ExpressionEngine; 040import org.apache.commons.configuration2.tree.ImmutableNode; 041import org.apache.commons.configuration2.tree.NodeCombiner; 042 043/** 044 * <p> 045 * DynamicCombinedConfiguration allows a set of CombinedConfigurations to be 046 * used. 047 * </p> 048 * <p> 049 * Each CombinedConfiguration is referenced by a key that is dynamically 050 * constructed from a key pattern on each call. The key pattern will be resolved 051 * using the configured ConfigurationInterpolator. 052 * </p> 053 * <p> 054 * This Configuration implementation uses the configured {@code Synchronizer} to 055 * guard itself against concurrent access. If there are multiple threads 056 * accessing an instance concurrently, a fully functional {@code Synchronizer} 057 * implementation (e.g. {@code ReadWriteSynchronizer}) has to be used to ensure 058 * consistency and to avoid exceptions. The {@code Synchronizer} assigned to an 059 * instance is also passed to child configuration objects when they are created. 060 * </p> 061 * 062 * @since 1.6 063 * @version $Id: DynamicCombinedConfiguration.java 1842194 2018-09-27 22:24:23Z ggregory $ 064 */ 065public class DynamicCombinedConfiguration extends CombinedConfiguration 066{ 067 /** 068 * Stores the current configuration for each involved thread. This value is 069 * set at the beginning of an operation and removed at the end. 070 */ 071 private static final ThreadLocal<CurrentConfigHolder> CURRENT_CONFIG = 072 new ThreadLocal<>(); 073 074 /** The CombinedConfigurations */ 075 private final ConcurrentMap<String, CombinedConfiguration> configs = 076 new ConcurrentHashMap<>(); 077 078 /** Stores a list with the contained configurations. */ 079 private final List<ConfigData> configurations = new ArrayList<>(); 080 081 /** Stores a map with the named configurations. */ 082 private final Map<String, Configuration> namedConfigurations = 083 new HashMap<>(); 084 085 /** The key pattern for the CombinedConfiguration map */ 086 private String keyPattern; 087 088 /** Stores the combiner. */ 089 private NodeCombiner nodeCombiner; 090 091 /** The name of the logger to use for each CombinedConfiguration */ 092 private String loggerName = DynamicCombinedConfiguration.class.getName(); 093 094 /** The object for handling variable substitution in key patterns. */ 095 private final ConfigurationInterpolator localSubst; 096 097 /** 098 * Creates a new instance of {@code DynamicCombinedConfiguration} and 099 * initializes the combiner to be used. 100 * 101 * @param comb the node combiner (can be <b>null</b>, then a union combiner 102 * is used as default) 103 */ 104 public DynamicCombinedConfiguration(final NodeCombiner comb) 105 { 106 super(); 107 setNodeCombiner(comb); 108 initLogger(new ConfigurationLogger(DynamicCombinedConfiguration.class)); 109 localSubst = initLocalInterpolator(); 110 } 111 112 /** 113 * Creates a new instance of {@code DynamicCombinedConfiguration} that uses 114 * a union combiner. 115 * 116 * @see org.apache.commons.configuration2.tree.UnionCombiner 117 */ 118 public DynamicCombinedConfiguration() 119 { 120 super(); 121 initLogger(new ConfigurationLogger(DynamicCombinedConfiguration.class)); 122 localSubst = initLocalInterpolator(); 123 } 124 125 public void setKeyPattern(final String pattern) 126 { 127 this.keyPattern = pattern; 128 } 129 130 public String getKeyPattern() 131 { 132 return this.keyPattern; 133 } 134 135 /** 136 * Set the name of the Logger to use on each CombinedConfiguration. 137 * @param name The Logger name. 138 */ 139 public void setLoggerName(final String name) 140 { 141 this.loggerName = name; 142 } 143 144 /** 145 * Returns the node combiner that is used for creating the combined node 146 * structure. 147 * 148 * @return the node combiner 149 */ 150 @Override 151 public NodeCombiner getNodeCombiner() 152 { 153 return nodeCombiner; 154 } 155 156 /** 157 * Sets the node combiner. This object will be used when the combined node 158 * structure is to be constructed. It must not be <b>null</b>, otherwise an 159 * {@code IllegalArgumentException} exception is thrown. Changing the 160 * node combiner causes an invalidation of this combined configuration, so 161 * that the new combiner immediately takes effect. 162 * 163 * @param nodeCombiner the node combiner 164 */ 165 @Override 166 public void setNodeCombiner(final NodeCombiner nodeCombiner) 167 { 168 if (nodeCombiner == null) 169 { 170 throw new IllegalArgumentException( 171 "Node combiner must not be null!"); 172 } 173 this.nodeCombiner = nodeCombiner; 174 invalidateAll(); 175 } 176 /** 177 * Adds a new configuration to this combined configuration. It is possible 178 * (but not mandatory) to give the new configuration a name. This name must 179 * be unique, otherwise a {@code ConfigurationRuntimeException} will 180 * be thrown. With the optional {@code at} argument you can specify 181 * where in the resulting node structure the content of the added 182 * configuration should appear. This is a string that uses dots as property 183 * delimiters (independent on the current expression engine). For instance 184 * if you pass in the string {@code "database.tables"}, 185 * all properties of the added configuration will occur in this branch. 186 * 187 * @param config the configuration to add (must not be <b>null</b>) 188 * @param name the name of this configuration (can be <b>null</b>) 189 * @param at the position of this configuration in the combined tree (can be 190 * <b>null</b>) 191 */ 192 @Override 193 public void addConfiguration(final Configuration config, final String name, 194 final String at) 195 { 196 beginWrite(true); 197 try 198 { 199 final ConfigData cd = new ConfigData(config, name, at); 200 configurations.add(cd); 201 if (name != null) 202 { 203 namedConfigurations.put(name, config); 204 } 205 206 // clear cache of all child configurations 207 configs.clear(); 208 } 209 finally 210 { 211 endWrite(); 212 } 213 } 214 /** 215 * Returns the number of configurations that are contained in this combined 216 * configuration. 217 * 218 * @return the number of contained configurations 219 */ 220 @Override 221 public int getNumberOfConfigurations() 222 { 223 beginRead(false); 224 try 225 { 226 return configurations.size(); 227 } 228 finally 229 { 230 endRead(); 231 } 232 } 233 234 /** 235 * Returns the configuration at the specified index. The contained 236 * configurations are numbered in the order they were added to this combined 237 * configuration. The index of the first configuration is 0. 238 * 239 * @param index the index 240 * @return the configuration at this index 241 */ 242 @Override 243 public Configuration getConfiguration(final int index) 244 { 245 beginRead(false); 246 try 247 { 248 final ConfigData cd = configurations.get(index); 249 return cd.getConfiguration(); 250 } 251 finally 252 { 253 endRead(); 254 } 255 } 256 257 /** 258 * Returns the configuration with the given name. This can be <b>null</b> 259 * if no such configuration exists. 260 * 261 * @param name the name of the configuration 262 * @return the configuration with this name 263 */ 264 @Override 265 public Configuration getConfiguration(final String name) 266 { 267 beginRead(false); 268 try 269 { 270 return namedConfigurations.get(name); 271 } 272 finally 273 { 274 endRead(); 275 } 276 } 277 278 /** 279 * Returns a set with the names of all configurations contained in this 280 * combined configuration. Of course here are only these configurations 281 * listed, for which a name was specified when they were added. 282 * 283 * @return a set with the names of the contained configurations (never 284 * <b>null</b>) 285 */ 286 @Override 287 public Set<String> getConfigurationNames() 288 { 289 beginRead(false); 290 try 291 { 292 return namedConfigurations.keySet(); 293 } 294 finally 295 { 296 endRead(); 297 } 298 } 299 300 /** 301 * Removes the configuration with the specified name. 302 * 303 * @param name the name of the configuration to be removed 304 * @return the removed configuration (<b>null</b> if this configuration 305 * was not found) 306 */ 307 @Override 308 public Configuration removeConfiguration(final String name) 309 { 310 final Configuration conf = getConfiguration(name); 311 if (conf != null) 312 { 313 removeConfiguration(conf); 314 } 315 return conf; 316 } 317 318 /** 319 * Removes the specified configuration from this combined configuration. 320 * 321 * @param config the configuration to be removed 322 * @return a flag whether this configuration was found and could be removed 323 */ 324 @Override 325 public boolean removeConfiguration(final Configuration config) 326 { 327 beginWrite(false); 328 try 329 { 330 for (int index = 0; index < getNumberOfConfigurations(); index++) 331 { 332 if (configurations.get(index).getConfiguration() == config) 333 { 334 removeConfigurationAt(index); 335 return true; 336 } 337 } 338 339 return false; 340 } 341 finally 342 { 343 endWrite(); 344 } 345 } 346 347 /** 348 * Removes the configuration at the specified index. 349 * 350 * @param index the index 351 * @return the removed configuration 352 */ 353 @Override 354 public Configuration removeConfigurationAt(final int index) 355 { 356 beginWrite(false); 357 try 358 { 359 final ConfigData cd = configurations.remove(index); 360 if (cd.getName() != null) 361 { 362 namedConfigurations.remove(cd.getName()); 363 } 364 return cd.getConfiguration(); 365 } 366 finally 367 { 368 endWrite(); 369 } 370 } 371 372 @Override 373 protected void addPropertyInternal(final String key, final Object value) 374 { 375 this.getCurrentConfig().addProperty(key, value); 376 } 377 378 @Override 379 protected void clearInternal() 380 { 381 if (configs != null) 382 { 383 this.getCurrentConfig().clear(); 384 } 385 } 386 387 @Override 388 protected void clearPropertyDirect(final String key) 389 { 390 this.getCurrentConfig().clearProperty(key); 391 } 392 393 @Override 394 protected boolean containsKeyInternal(final String key) 395 { 396 return this.getCurrentConfig().containsKey(key); 397 } 398 399 @Override 400 public BigDecimal getBigDecimal(final String key, final BigDecimal defaultValue) 401 { 402 return this.getCurrentConfig().getBigDecimal(key, defaultValue); 403 } 404 405 @Override 406 public BigDecimal getBigDecimal(final String key) 407 { 408 return this.getCurrentConfig().getBigDecimal(key); 409 } 410 411 @Override 412 public BigInteger getBigInteger(final String key, final BigInteger defaultValue) 413 { 414 return this.getCurrentConfig().getBigInteger(key, defaultValue); 415 } 416 417 @Override 418 public BigInteger getBigInteger(final String key) 419 { 420 return this.getCurrentConfig().getBigInteger(key); 421 } 422 423 @Override 424 public boolean getBoolean(final String key, final boolean defaultValue) 425 { 426 return this.getCurrentConfig().getBoolean(key, defaultValue); 427 } 428 429 @Override 430 public Boolean getBoolean(final String key, final Boolean defaultValue) 431 { 432 return this.getCurrentConfig().getBoolean(key, defaultValue); 433 } 434 435 @Override 436 public boolean getBoolean(final String key) 437 { 438 return this.getCurrentConfig().getBoolean(key); 439 } 440 441 @Override 442 public byte getByte(final String key, final byte defaultValue) 443 { 444 return this.getCurrentConfig().getByte(key, defaultValue); 445 } 446 447 @Override 448 public Byte getByte(final String key, final Byte defaultValue) 449 { 450 return this.getCurrentConfig().getByte(key, defaultValue); 451 } 452 453 @Override 454 public byte getByte(final String key) 455 { 456 return this.getCurrentConfig().getByte(key); 457 } 458 459 @Override 460 public double getDouble(final String key, final double defaultValue) 461 { 462 return this.getCurrentConfig().getDouble(key, defaultValue); 463 } 464 465 @Override 466 public Double getDouble(final String key, final Double defaultValue) 467 { 468 return this.getCurrentConfig().getDouble(key, defaultValue); 469 } 470 471 @Override 472 public double getDouble(final String key) 473 { 474 return this.getCurrentConfig().getDouble(key); 475 } 476 477 @Override 478 public float getFloat(final String key, final float defaultValue) 479 { 480 return this.getCurrentConfig().getFloat(key, defaultValue); 481 } 482 483 @Override 484 public Float getFloat(final String key, final Float defaultValue) 485 { 486 return this.getCurrentConfig().getFloat(key, defaultValue); 487 } 488 489 @Override 490 public float getFloat(final String key) 491 { 492 return this.getCurrentConfig().getFloat(key); 493 } 494 495 @Override 496 public int getInt(final String key, final int defaultValue) 497 { 498 return this.getCurrentConfig().getInt(key, defaultValue); 499 } 500 501 @Override 502 public int getInt(final String key) 503 { 504 return this.getCurrentConfig().getInt(key); 505 } 506 507 @Override 508 public Integer getInteger(final String key, final Integer defaultValue) 509 { 510 return this.getCurrentConfig().getInteger(key, defaultValue); 511 } 512 513 @Override 514 protected Iterator<String> getKeysInternal() 515 { 516 return this.getCurrentConfig().getKeys(); 517 } 518 519 @Override 520 protected Iterator<String> getKeysInternal(final String prefix) 521 { 522 return this.getCurrentConfig().getKeys(prefix); 523 } 524 525 @Override 526 public List<Object> getList(final String key, final List<?> defaultValue) 527 { 528 return this.getCurrentConfig().getList(key, defaultValue); 529 } 530 531 @Override 532 public List<Object> getList(final String key) 533 { 534 return this.getCurrentConfig().getList(key); 535 } 536 537 @Override 538 public long getLong(final String key, final long defaultValue) 539 { 540 return this.getCurrentConfig().getLong(key, defaultValue); 541 } 542 543 @Override 544 public Long getLong(final String key, final Long defaultValue) 545 { 546 return this.getCurrentConfig().getLong(key, defaultValue); 547 } 548 549 @Override 550 public long getLong(final String key) 551 { 552 return this.getCurrentConfig().getLong(key); 553 } 554 555 @Override 556 public Properties getProperties(final String key) 557 { 558 return this.getCurrentConfig().getProperties(key); 559 } 560 561 @Override 562 protected Object getPropertyInternal(final String key) 563 { 564 return this.getCurrentConfig().getProperty(key); 565 } 566 567 @Override 568 public short getShort(final String key, final short defaultValue) 569 { 570 return this.getCurrentConfig().getShort(key, defaultValue); 571 } 572 573 @Override 574 public Short getShort(final String key, final Short defaultValue) 575 { 576 return this.getCurrentConfig().getShort(key, defaultValue); 577 } 578 579 @Override 580 public short getShort(final String key) 581 { 582 return this.getCurrentConfig().getShort(key); 583 } 584 585 @Override 586 public String getString(final String key, final String defaultValue) 587 { 588 return this.getCurrentConfig().getString(key, defaultValue); 589 } 590 591 @Override 592 public String getString(final String key) 593 { 594 return this.getCurrentConfig().getString(key); 595 } 596 597 @Override 598 public String[] getStringArray(final String key) 599 { 600 return this.getCurrentConfig().getStringArray(key); 601 } 602 603 @Override 604 protected boolean isEmptyInternal() 605 { 606 return this.getCurrentConfig().isEmpty(); 607 } 608 609 @Override 610 protected int sizeInternal() 611 { 612 return this.getCurrentConfig().size(); 613 } 614 615 @Override 616 protected void setPropertyInternal(final String key, final Object value) 617 { 618 if (configs != null) 619 { 620 this.getCurrentConfig().setProperty(key, value); 621 } 622 } 623 624 @Override 625 public Configuration subset(final String prefix) 626 { 627 return this.getCurrentConfig().subset(prefix); 628 } 629 630 @Override 631 public ExpressionEngine getExpressionEngine() 632 { 633 return super.getExpressionEngine(); 634 } 635 636 @Override 637 public void setExpressionEngine(final ExpressionEngine expressionEngine) 638 { 639 super.setExpressionEngine(expressionEngine); 640 } 641 642 @Override 643 protected void addNodesInternal(final String key, final Collection<? extends ImmutableNode> nodes) 644 { 645 this.getCurrentConfig().addNodes(key, nodes); 646 } 647 648 @Override 649 public HierarchicalConfiguration<ImmutableNode> configurationAt(final String key, final boolean supportUpdates) 650 { 651 return this.getCurrentConfig().configurationAt(key, supportUpdates); 652 } 653 654 @Override 655 public HierarchicalConfiguration<ImmutableNode> configurationAt(final String key) 656 { 657 return this.getCurrentConfig().configurationAt(key); 658 } 659 660 @Override 661 public List<HierarchicalConfiguration<ImmutableNode>> configurationsAt(final String key) 662 { 663 return this.getCurrentConfig().configurationsAt(key); 664 } 665 666 @Override 667 protected Object clearTreeInternal(final String key) 668 { 669 this.getCurrentConfig().clearTree(key); 670 return Collections.emptyList(); 671 } 672 673 @Override 674 protected int getMaxIndexInternal(final String key) 675 { 676 return this.getCurrentConfig().getMaxIndex(key); 677 } 678 679 @Override 680 public Configuration interpolatedConfiguration() 681 { 682 return this.getCurrentConfig().interpolatedConfiguration(); 683 } 684 685 686 /** 687 * Returns the configuration source, in which the specified key is defined. 688 * This method will determine the configuration node that is identified by 689 * the given key. The following constellations are possible: 690 * <ul> 691 * <li>If no node object is found for this key, <b>null</b> is returned.</li> 692 * <li>If the key maps to multiple nodes belonging to different 693 * configuration sources, a {@code IllegalArgumentException} is 694 * thrown (in this case no unique source can be determined).</li> 695 * <li>If exactly one node is found for the key, the (child) configuration 696 * object, to which the node belongs is determined and returned.</li> 697 * <li>For keys that have been added directly to this combined 698 * configuration and that do not belong to the namespaces defined by 699 * existing child configurations this configuration will be returned.</li> 700 * </ul> 701 * 702 * @param key the key of a configuration property 703 * @return the configuration, to which this property belongs or <b>null</b> 704 * if the key cannot be resolved 705 * @throws IllegalArgumentException if the key maps to multiple properties 706 * and the source cannot be determined, or if the key is <b>null</b> 707 */ 708 @Override 709 public Configuration getSource(final String key) 710 { 711 if (key == null) 712 { 713 throw new IllegalArgumentException("Key must not be null!"); 714 } 715 return getCurrentConfig().getSource(key); 716 } 717 718 @Override 719 public void clearEventListeners() 720 { 721 for (final CombinedConfiguration cc : configs.values()) 722 { 723 cc.clearEventListeners(); 724 } 725 super.clearEventListeners(); 726 } 727 728 @Override 729 public <T extends Event> void addEventListener(final EventType<T> eventType, 730 final EventListener<? super T> listener) 731 { 732 for (final CombinedConfiguration cc : configs.values()) 733 { 734 cc.addEventListener(eventType, listener); 735 } 736 super.addEventListener(eventType, listener); 737 } 738 739 @Override 740 public <T extends Event> boolean removeEventListener( 741 final EventType<T> eventType, final EventListener<? super T> listener) 742 { 743 for (final CombinedConfiguration cc : configs.values()) 744 { 745 cc.removeEventListener(eventType, listener); 746 } 747 return super.removeEventListener(eventType, listener); 748 } 749 750 @Override 751 public void clearErrorListeners() 752 { 753 for (final CombinedConfiguration cc : configs.values()) 754 { 755 cc.clearErrorListeners(); 756 } 757 super.clearErrorListeners(); 758 } 759 760 /** 761 * Returns a copy of this object. This implementation performs a deep clone, 762 * i.e. all contained configurations will be cloned, too. For this to work, 763 * all contained configurations must be cloneable. Registered event 764 * listeners won't be cloned. The clone will use the same node combiner than 765 * the original. 766 * 767 * @return the copied object 768 */ 769 @Override 770 public Object clone() 771 { 772 return super.clone(); 773 } 774 775 /** 776 * Invalidates the current combined configuration. This means that the next time a 777 * property is accessed the combined node structure must be re-constructed. 778 * Invalidation of a combined configuration also means that an event of type 779 * {@code EVENT_COMBINED_INVALIDATE} is fired. Note that while other 780 * events most times appear twice (once before and once after an update), 781 * this event is only fired once (after update). 782 */ 783 @Override 784 public void invalidate() 785 { 786 getCurrentConfig().invalidate(); 787 } 788 789 public void invalidateAll() 790 { 791 for (final CombinedConfiguration cc : configs.values()) 792 { 793 cc.invalidate(); 794 } 795 } 796 797 /** 798 * {@inheritDoc} This implementation ensures that the current configuration 799 * is initialized. The lock counter is increased. 800 */ 801 @Override 802 protected void beginRead(final boolean optimize) 803 { 804 final CurrentConfigHolder cch = ensureCurrentConfiguration(); 805 cch.incrementLockCount(); 806 if (!optimize && cch.getCurrentConfiguration() == null) 807 { 808 // delegate to beginWrite() which creates the child configuration 809 beginWrite(false); 810 endWrite(); 811 } 812 813 // This actually uses our own synchronizer 814 cch.getCurrentConfiguration().beginRead(optimize); 815 } 816 817 /** 818 * {@inheritDoc} This implementation ensures that the current configuration 819 * is initialized. If necessary, a new child configuration instance is 820 * created. 821 */ 822 @Override 823 protected void beginWrite(final boolean optimize) 824 { 825 final CurrentConfigHolder cch = ensureCurrentConfiguration(); 826 cch.incrementLockCount(); 827 828 super.beginWrite(optimize); 829 if (!optimize && cch.getCurrentConfiguration() == null) 830 { 831 cch.setCurrentConfiguration(createChildConfiguration()); 832 configs.put(cch.getKey(), cch.getCurrentConfiguration()); 833 initChildConfiguration(cch.getCurrentConfiguration()); 834 } 835 } 836 837 /** 838 * {@inheritDoc} This implementation clears the current configuration if 839 * necessary. 840 */ 841 @Override 842 protected void endRead() 843 { 844 CURRENT_CONFIG.get().getCurrentConfiguration().endRead(); 845 releaseLock(); 846 } 847 848 /** 849 * {@inheritDoc} This implementation clears the current configuration if 850 * necessary. 851 */ 852 @Override 853 protected void endWrite() 854 { 855 super.endWrite(); 856 releaseLock(); 857 } 858 859 /** 860 * Decrements the lock count of the current configuration holder. If it 861 * reaches 0, the current configuration is removed. (It is then reevaluated 862 * when the next operation starts.) 863 */ 864 private void releaseLock() 865 { 866 final CurrentConfigHolder cch = CURRENT_CONFIG.get(); 867 assert cch != null : "No current configuration!"; 868 if (cch.decrementLockCountAndCheckRelease()) 869 { 870 CURRENT_CONFIG.remove(); 871 } 872 } 873 874 /** 875 * Returns the current configuration. This configuration was initialized at 876 * the beginning of an operation and stored in a thread-local variable. Some 877 * methods of this class call this method directly without requesting a lock 878 * before. To deal with this, we always request an additional read lock. 879 * 880 * @return the current configuration 881 */ 882 private CombinedConfiguration getCurrentConfig() 883 { 884 CombinedConfiguration config; 885 String key; 886 beginRead(false); 887 try 888 { 889 config = CURRENT_CONFIG.get().getCurrentConfiguration(); 890 key = CURRENT_CONFIG.get().getKey(); 891 } 892 finally 893 { 894 endRead(); 895 } 896 897 if (getLogger().isDebugEnabled()) 898 { 899 getLogger().debug("Returning config for " + key + ": " + config); 900 } 901 return config; 902 } 903 904 /** 905 * Creates a new, uninitialized child configuration. 906 * 907 * @return the new child configuration 908 */ 909 private CombinedConfiguration createChildConfiguration() 910 { 911 return new CombinedConfiguration(getNodeCombiner()); 912 } 913 914 /** 915 * Initializes a newly created child configuration. This method copies a 916 * bunch of settings from this instance to the child configuration. 917 * 918 * @param config the child configuration to be initialized 919 */ 920 private void initChildConfiguration(final CombinedConfiguration config) 921 { 922 if (loggerName != null) 923 { 924 config.setLogger(new ConfigurationLogger(loggerName)); 925 } 926 config.setExpressionEngine(this.getExpressionEngine()); 927 config.setConversionExpressionEngine(getConversionExpressionEngine()); 928 config.setListDelimiterHandler(getListDelimiterHandler()); 929 copyEventListeners(config); 930 for (final ConfigData data : configurations) 931 { 932 config.addConfiguration(data.getConfiguration(), data.getName(), 933 data.getAt()); 934 } 935 config.setSynchronizer(getSynchronizer()); 936 } 937 938 /** 939 * Creates a {@code ConfigurationInterpolator} instance for performing local 940 * variable substitutions. This implementation returns an object which 941 * shares the prefix lookups from this configuration's 942 * {@code ConfigurationInterpolator}, but does not define any other lookups. 943 * 944 * @return the {@code ConfigurationInterpolator} 945 */ 946 private ConfigurationInterpolator initLocalInterpolator() 947 { 948 return new ConfigurationInterpolator() 949 { 950 @Override 951 protected Lookup fetchLookupForPrefix(final String prefix) 952 { 953 return ConfigurationInterpolator 954 .nullSafeLookup(getInterpolator().getLookups().get( 955 prefix)); 956 } 957 }; 958 } 959 960 /** 961 * Checks whether the current configuration is set. If not, a 962 * {@code CurrentConfigHolder} is now created and initialized, and 963 * associated with the current thread. The member for the current 964 * configuration is undefined if for the current key no configuration exists 965 * yet. 966 * 967 * @return the {@code CurrentConfigHolder} instance for the current thread 968 */ 969 private CurrentConfigHolder ensureCurrentConfiguration() 970 { 971 CurrentConfigHolder cch = CURRENT_CONFIG.get(); 972 if (cch == null) 973 { 974 final String key = String.valueOf(localSubst.interpolate(keyPattern)); 975 cch = new CurrentConfigHolder(key); 976 cch.setCurrentConfiguration(configs.get(key)); 977 CURRENT_CONFIG.set(cch); 978 } 979 return cch; 980 } 981 982 /** 983 * Internal class that identifies each Configuration. 984 */ 985 static class ConfigData 986 { 987 /** Stores a reference to the configuration. */ 988 private final Configuration configuration; 989 990 /** Stores the name under which the configuration is stored. */ 991 private final String name; 992 993 /** Stores the at string.*/ 994 private final String at; 995 996 /** 997 * Creates a new instance of {@code ConfigData} and initializes 998 * it. 999 * 1000 * @param config the configuration 1001 * @param n the name 1002 * @param at the at position 1003 */ 1004 public ConfigData(final Configuration config, final String n, final String at) 1005 { 1006 configuration = config; 1007 name = n; 1008 this.at = at; 1009 } 1010 1011 /** 1012 * Returns the stored configuration. 1013 * 1014 * @return the configuration 1015 */ 1016 public Configuration getConfiguration() 1017 { 1018 return configuration; 1019 } 1020 1021 /** 1022 * Returns the configuration's name. 1023 * 1024 * @return the name 1025 */ 1026 public String getName() 1027 { 1028 return name; 1029 } 1030 1031 /** 1032 * Returns the at position of this configuration. 1033 * 1034 * @return the at position 1035 */ 1036 public String getAt() 1037 { 1038 return at; 1039 } 1040 1041 } 1042 1043 /** 1044 * A simple data class holding information about the current configuration 1045 * while an operation for a thread is processed. 1046 */ 1047 private static class CurrentConfigHolder 1048 { 1049 /** Stores the current configuration of the current thread. */ 1050 private CombinedConfiguration currentConfiguration; 1051 1052 /** 1053 * Stores the key of the configuration evaluated for the current thread 1054 * at the beginning of an operation. 1055 */ 1056 private final String key; 1057 1058 /** A counter for reentrant locks. */ 1059 private int lockCount; 1060 1061 /** 1062 * Creates a new instance of {@code CurrentConfigHolder} and initializes 1063 * it with the key for the current configuration. 1064 * 1065 * @param curKey the current key 1066 */ 1067 public CurrentConfigHolder(final String curKey) 1068 { 1069 key = curKey; 1070 } 1071 1072 /** 1073 * Returns the current configuration. 1074 * 1075 * @return the current configuration 1076 */ 1077 public CombinedConfiguration getCurrentConfiguration() 1078 { 1079 return currentConfiguration; 1080 } 1081 1082 /** 1083 * Sets the current configuration. 1084 * 1085 * @param currentConfiguration the current configuration 1086 */ 1087 public void setCurrentConfiguration( 1088 final CombinedConfiguration currentConfiguration) 1089 { 1090 this.currentConfiguration = currentConfiguration; 1091 } 1092 1093 /** 1094 * Returns the current key. 1095 * 1096 * @return the current key 1097 */ 1098 public String getKey() 1099 { 1100 return key; 1101 } 1102 1103 /** 1104 * Increments the lock counter. 1105 */ 1106 public void incrementLockCount() 1107 { 1108 lockCount++; 1109 } 1110 1111 /** 1112 * Decrements the lock counter and checks whether it has reached 0. In 1113 * this cause, the operation is complete, and the lock can be released. 1114 * 1115 * @return <b>true</b> if the lock count reaches 0, <b>false</b> 1116 * otherwise 1117 */ 1118 public boolean decrementLockCountAndCheckRelease() 1119 { 1120 return --lockCount == 0; 1121 } 1122 } 1123}