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