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