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