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