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.configuration; 018 019import java.math.BigDecimal; 020import java.math.BigInteger; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027import java.util.Properties; 028import java.util.Set; 029import java.util.concurrent.ConcurrentHashMap; 030import java.util.concurrent.ConcurrentMap; 031 032import org.apache.commons.configuration.event.ConfigurationErrorListener; 033import org.apache.commons.configuration.event.ConfigurationListener; 034import org.apache.commons.configuration.interpol.ConfigurationInterpolator; 035import org.apache.commons.configuration.tree.ConfigurationNode; 036import org.apache.commons.configuration.tree.ExpressionEngine; 037import org.apache.commons.configuration.tree.NodeCombiner; 038import org.apache.commons.lang.text.StrSubstitutor; 039import org.apache.commons.logging.Log; 040import org.apache.commons.logging.LogFactory; 041 042/** 043 * DynamicCombinedConfiguration allows a set of CombinedConfigurations to be used. Each CombinedConfiguration 044 * is referenced by a key that is dynamically constructed from a key pattern on each call. The key pattern 045 * will be resolved using the configured ConfigurationInterpolator. 046 * @since 1.6 047 * @author <a 048 * href="http://commons.apache.org/configuration/team-list.html">Commons 049 * Configuration team</a> 050 * @version $Id: DynamicCombinedConfiguration.java 1534064 2013-10-21 08:44:33Z henning $ 051 */ 052public class DynamicCombinedConfiguration extends CombinedConfiguration 053{ 054 /** 055 * Prevent recursion while resolving unprefixed properties. 056 */ 057 private static ThreadLocal<Boolean> recursive = new ThreadLocal<Boolean>() 058 { 059 @Override 060 protected synchronized Boolean initialValue() 061 { 062 return Boolean.FALSE; 063 } 064 }; 065 066 /** The CombinedConfigurations */ 067 private final ConcurrentMap<String, CombinedConfiguration> configs = 068 new ConcurrentHashMap<String, CombinedConfiguration>(); 069 070 /** Stores a list with the contained configurations. */ 071 private List<ConfigData> configurations = new ArrayList<ConfigData>(); 072 073 /** Stores a map with the named configurations. */ 074 private Map<String, AbstractConfiguration> namedConfigurations = 075 new HashMap<String, AbstractConfiguration>(); 076 077 /** The key pattern for the CombinedConfiguration map */ 078 private String keyPattern; 079 080 /** Stores the combiner. */ 081 private NodeCombiner nodeCombiner; 082 083 /** The name of the logger to use for each CombinedConfiguration */ 084 private String loggerName = DynamicCombinedConfiguration.class.getName(); 085 086 /** The object for handling variable substitution in key patterns. */ 087 private StrSubstitutor localSubst = new StrSubstitutor(new ConfigurationInterpolator()); 088 089 /** 090 * Creates a new instance of {@code DynamicCombinedConfiguration} and 091 * initializes the combiner to be used. 092 * 093 * @param comb the node combiner (can be <b>null</b>, then a union combiner 094 * is used as default) 095 */ 096 public DynamicCombinedConfiguration(NodeCombiner comb) 097 { 098 super(); 099 setNodeCombiner(comb); 100 setIgnoreReloadExceptions(false); 101 setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class)); 102 } 103 104 /** 105 * Creates a new instance of {@code DynamicCombinedConfiguration} that uses 106 * a union combiner. 107 * 108 * @see org.apache.commons.configuration.tree.UnionCombiner 109 */ 110 public DynamicCombinedConfiguration() 111 { 112 super(); 113 setIgnoreReloadExceptions(false); 114 setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class)); 115 } 116 117 public void setKeyPattern(String pattern) 118 { 119 this.keyPattern = pattern; 120 } 121 122 public String getKeyPattern() 123 { 124 return this.keyPattern; 125 } 126 127 /** 128 * Set the name of the Logger to use on each CombinedConfiguration. 129 * @param name The Logger name. 130 */ 131 public void setLoggerName(String name) 132 { 133 this.loggerName = name; 134 } 135 136 /** 137 * Returns the node combiner that is used for creating the combined node 138 * structure. 139 * 140 * @return the node combiner 141 */ 142 @Override 143 public NodeCombiner getNodeCombiner() 144 { 145 return nodeCombiner; 146 } 147 148 /** 149 * Sets the node combiner. This object will be used when the combined node 150 * structure is to be constructed. It must not be <b>null</b>, otherwise an 151 * {@code IllegalArgumentException} exception is thrown. Changing the 152 * node combiner causes an invalidation of this combined configuration, so 153 * that the new combiner immediately takes effect. 154 * 155 * @param nodeCombiner the node combiner 156 */ 157 @Override 158 public void setNodeCombiner(NodeCombiner nodeCombiner) 159 { 160 if (nodeCombiner == null) 161 { 162 throw new IllegalArgumentException( 163 "Node combiner must not be null!"); 164 } 165 this.nodeCombiner = nodeCombiner; 166 invalidateAll(); 167 } 168 /** 169 * Adds a new configuration to this combined configuration. It is possible 170 * (but not mandatory) to give the new configuration a name. This name must 171 * be unique, otherwise a {@code ConfigurationRuntimeException} will 172 * be thrown. With the optional {@code at} argument you can specify 173 * where in the resulting node structure the content of the added 174 * configuration should appear. This is a string that uses dots as property 175 * delimiters (independent on the current expression engine). For instance 176 * if you pass in the string {@code "database.tables"}, 177 * all properties of the added configuration will occur in this branch. 178 * 179 * @param config the configuration to add (must not be <b>null</b>) 180 * @param name the name of this configuration (can be <b>null</b>) 181 * @param at the position of this configuration in the combined tree (can be 182 * <b>null</b>) 183 */ 184 @Override 185 public void addConfiguration(AbstractConfiguration config, String name, 186 String at) 187 { 188 ConfigData cd = new ConfigData(config, name, at); 189 configurations.add(cd); 190 if (name != null) 191 { 192 namedConfigurations.put(name, config); 193 } 194 } 195 /** 196 * Returns the number of configurations that are contained in this combined 197 * configuration. 198 * 199 * @return the number of contained configurations 200 */ 201 @Override 202 public int getNumberOfConfigurations() 203 { 204 return configurations.size(); 205 } 206 207 /** 208 * Returns the configuration at the specified index. The contained 209 * configurations are numbered in the order they were added to this combined 210 * configuration. The index of the first configuration is 0. 211 * 212 * @param index the index 213 * @return the configuration at this index 214 */ 215 @Override 216 public Configuration getConfiguration(int index) 217 { 218 ConfigData cd = configurations.get(index); 219 return cd.getConfiguration(); 220 } 221 222 /** 223 * Returns the configuration with the given name. This can be <b>null</b> 224 * if no such configuration exists. 225 * 226 * @param name the name of the configuration 227 * @return the configuration with this name 228 */ 229 @Override 230 public Configuration getConfiguration(String name) 231 { 232 return namedConfigurations.get(name); 233 } 234 235 /** 236 * Returns a set with the names of all configurations contained in this 237 * combined configuration. Of course here are only these configurations 238 * listed, for which a name was specified when they were added. 239 * 240 * @return a set with the names of the contained configurations (never 241 * <b>null</b>) 242 */ 243 @Override 244 public Set<String> getConfigurationNames() 245 { 246 return namedConfigurations.keySet(); 247 } 248 249 /** 250 * Removes the configuration with the specified name. 251 * 252 * @param name the name of the configuration to be removed 253 * @return the removed configuration (<b>null</b> if this configuration 254 * was not found) 255 */ 256 @Override 257 public Configuration removeConfiguration(String name) 258 { 259 Configuration conf = getConfiguration(name); 260 if (conf != null) 261 { 262 removeConfiguration(conf); 263 } 264 return conf; 265 } 266 267 /** 268 * Removes the specified configuration from this combined configuration. 269 * 270 * @param config the configuration to be removed 271 * @return a flag whether this configuration was found and could be removed 272 */ 273 @Override 274 public boolean removeConfiguration(Configuration config) 275 { 276 for (int index = 0; index < getNumberOfConfigurations(); index++) 277 { 278 if (configurations.get(index).getConfiguration() == config) 279 { 280 removeConfigurationAt(index); 281 282 } 283 } 284 285 return super.removeConfiguration(config); 286 } 287 288 /** 289 * Removes the configuration at the specified index. 290 * 291 * @param index the index 292 * @return the removed configuration 293 */ 294 @Override 295 public Configuration removeConfigurationAt(int index) 296 { 297 ConfigData cd = configurations.remove(index); 298 if (cd.getName() != null) 299 { 300 namedConfigurations.remove(cd.getName()); 301 } 302 return super.removeConfigurationAt(index); 303 } 304 /** 305 * Returns the configuration root node of this combined configuration. This 306 * method will construct a combined node structure using the current node 307 * combiner if necessary. 308 * 309 * @return the combined root node 310 */ 311 @Override 312 public ConfigurationNode getRootNode() 313 { 314 return getCurrentConfig().getRootNode(); 315 } 316 317 @Override 318 public void setRootNode(ConfigurationNode rootNode) 319 { 320 if (configs != null) 321 { 322 this.getCurrentConfig().setRootNode(rootNode); 323 } 324 else 325 { 326 super.setRootNode(rootNode); 327 } 328 } 329 330 @Override 331 public void addProperty(String key, Object value) 332 { 333 this.getCurrentConfig().addProperty(key, value); 334 } 335 336 @Override 337 public void clear() 338 { 339 if (configs != null) 340 { 341 this.getCurrentConfig().clear(); 342 } 343 } 344 345 @Override 346 public void clearProperty(String key) 347 { 348 this.getCurrentConfig().clearProperty(key); 349 } 350 351 @Override 352 public boolean containsKey(String key) 353 { 354 return this.getCurrentConfig().containsKey(key); 355 } 356 357 @Override 358 public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) 359 { 360 return this.getCurrentConfig().getBigDecimal(key, defaultValue); 361 } 362 363 @Override 364 public BigDecimal getBigDecimal(String key) 365 { 366 return this.getCurrentConfig().getBigDecimal(key); 367 } 368 369 @Override 370 public BigInteger getBigInteger(String key, BigInteger defaultValue) 371 { 372 return this.getCurrentConfig().getBigInteger(key, defaultValue); 373 } 374 375 @Override 376 public BigInteger getBigInteger(String key) 377 { 378 return this.getCurrentConfig().getBigInteger(key); 379 } 380 381 @Override 382 public boolean getBoolean(String key, boolean defaultValue) 383 { 384 return this.getCurrentConfig().getBoolean(key, defaultValue); 385 } 386 387 @Override 388 public Boolean getBoolean(String key, Boolean defaultValue) 389 { 390 return this.getCurrentConfig().getBoolean(key, defaultValue); 391 } 392 393 @Override 394 public boolean getBoolean(String key) 395 { 396 return this.getCurrentConfig().getBoolean(key); 397 } 398 399 @Override 400 public byte getByte(String key, byte defaultValue) 401 { 402 return this.getCurrentConfig().getByte(key, defaultValue); 403 } 404 405 @Override 406 public Byte getByte(String key, Byte defaultValue) 407 { 408 return this.getCurrentConfig().getByte(key, defaultValue); 409 } 410 411 @Override 412 public byte getByte(String key) 413 { 414 return this.getCurrentConfig().getByte(key); 415 } 416 417 @Override 418 public double getDouble(String key, double defaultValue) 419 { 420 return this.getCurrentConfig().getDouble(key, defaultValue); 421 } 422 423 @Override 424 public Double getDouble(String key, Double defaultValue) 425 { 426 return this.getCurrentConfig().getDouble(key, defaultValue); 427 } 428 429 @Override 430 public double getDouble(String key) 431 { 432 return this.getCurrentConfig().getDouble(key); 433 } 434 435 @Override 436 public float getFloat(String key, float defaultValue) 437 { 438 return this.getCurrentConfig().getFloat(key, defaultValue); 439 } 440 441 @Override 442 public Float getFloat(String key, Float defaultValue) 443 { 444 return this.getCurrentConfig().getFloat(key, defaultValue); 445 } 446 447 @Override 448 public float getFloat(String key) 449 { 450 return this.getCurrentConfig().getFloat(key); 451 } 452 453 @Override 454 public int getInt(String key, int defaultValue) 455 { 456 return this.getCurrentConfig().getInt(key, defaultValue); 457 } 458 459 @Override 460 public int getInt(String key) 461 { 462 return this.getCurrentConfig().getInt(key); 463 } 464 465 @Override 466 public Integer getInteger(String key, Integer defaultValue) 467 { 468 return this.getCurrentConfig().getInteger(key, defaultValue); 469 } 470 471 @Override 472 public Iterator<String> getKeys() 473 { 474 return this.getCurrentConfig().getKeys(); 475 } 476 477 @Override 478 public Iterator<String> getKeys(String prefix) 479 { 480 return this.getCurrentConfig().getKeys(prefix); 481 } 482 483 @Override 484 public List<Object> getList(String key, List<?> defaultValue) 485 { 486 return this.getCurrentConfig().getList(key, defaultValue); 487 } 488 489 @Override 490 public List<Object> getList(String key) 491 { 492 return this.getCurrentConfig().getList(key); 493 } 494 495 @Override 496 public long getLong(String key, long defaultValue) 497 { 498 return this.getCurrentConfig().getLong(key, defaultValue); 499 } 500 501 @Override 502 public Long getLong(String key, Long defaultValue) 503 { 504 return this.getCurrentConfig().getLong(key, defaultValue); 505 } 506 507 @Override 508 public long getLong(String key) 509 { 510 return this.getCurrentConfig().getLong(key); 511 } 512 513 @Override 514 public Properties getProperties(String key) 515 { 516 return this.getCurrentConfig().getProperties(key); 517 } 518 519 @Override 520 public Object getProperty(String key) 521 { 522 return this.getCurrentConfig().getProperty(key); 523 } 524 525 @Override 526 public short getShort(String key, short defaultValue) 527 { 528 return this.getCurrentConfig().getShort(key, defaultValue); 529 } 530 531 @Override 532 public Short getShort(String key, Short defaultValue) 533 { 534 return this.getCurrentConfig().getShort(key, defaultValue); 535 } 536 537 @Override 538 public short getShort(String key) 539 { 540 return this.getCurrentConfig().getShort(key); 541 } 542 543 @Override 544 public String getString(String key, String defaultValue) 545 { 546 return this.getCurrentConfig().getString(key, defaultValue); 547 } 548 549 @Override 550 public String getString(String key) 551 { 552 return this.getCurrentConfig().getString(key); 553 } 554 555 @Override 556 public String[] getStringArray(String key) 557 { 558 return this.getCurrentConfig().getStringArray(key); 559 } 560 561 @Override 562 public boolean isEmpty() 563 { 564 return this.getCurrentConfig().isEmpty(); 565 } 566 567 @Override 568 public void setProperty(String key, Object value) 569 { 570 if (configs != null) 571 { 572 this.getCurrentConfig().setProperty(key, value); 573 } 574 } 575 576 @Override 577 public Configuration subset(String prefix) 578 { 579 return this.getCurrentConfig().subset(prefix); 580 } 581 582 @Override 583 public Node getRoot() 584 { 585 return this.getCurrentConfig().getRoot(); 586 } 587 588 @Override 589 public void setRoot(Node node) 590 { 591 if (configs != null) 592 { 593 this.getCurrentConfig().setRoot(node); 594 } 595 else 596 { 597 super.setRoot(node); 598 } 599 } 600 601 @Override 602 public ExpressionEngine getExpressionEngine() 603 { 604 return super.getExpressionEngine(); 605 } 606 607 @Override 608 public void setExpressionEngine(ExpressionEngine expressionEngine) 609 { 610 super.setExpressionEngine(expressionEngine); 611 } 612 613 @Override 614 public void addNodes(String key, Collection<? extends ConfigurationNode> nodes) 615 { 616 this.getCurrentConfig().addNodes(key, nodes); 617 } 618 619 @Override 620 public SubnodeConfiguration configurationAt(String key, boolean supportUpdates) 621 { 622 return this.getCurrentConfig().configurationAt(key, supportUpdates); 623 } 624 625 @Override 626 public SubnodeConfiguration configurationAt(String key) 627 { 628 return this.getCurrentConfig().configurationAt(key); 629 } 630 631 @Override 632 public List<HierarchicalConfiguration> configurationsAt(String key) 633 { 634 return this.getCurrentConfig().configurationsAt(key); 635 } 636 637 @Override 638 public void clearTree(String key) 639 { 640 this.getCurrentConfig().clearTree(key); 641 } 642 643 @Override 644 public int getMaxIndex(String key) 645 { 646 return this.getCurrentConfig().getMaxIndex(key); 647 } 648 649 @Override 650 public Configuration interpolatedConfiguration() 651 { 652 return this.getCurrentConfig().interpolatedConfiguration(); 653 } 654 655 656 /** 657 * Returns the configuration source, in which the specified key is defined. 658 * This method will determine the configuration node that is identified by 659 * the given key. The following constellations are possible: 660 * <ul> 661 * <li>If no node object is found for this key, <b>null</b> is returned.</li> 662 * <li>If the key maps to multiple nodes belonging to different 663 * configuration sources, a {@code IllegalArgumentException} is 664 * thrown (in this case no unique source can be determined).</li> 665 * <li>If exactly one node is found for the key, the (child) configuration 666 * object, to which the node belongs is determined and returned.</li> 667 * <li>For keys that have been added directly to this combined 668 * configuration and that do not belong to the namespaces defined by 669 * existing child configurations this configuration will be returned.</li> 670 * </ul> 671 * 672 * @param key the key of a configuration property 673 * @return the configuration, to which this property belongs or <b>null</b> 674 * if the key cannot be resolved 675 * @throws IllegalArgumentException if the key maps to multiple properties 676 * and the source cannot be determined, or if the key is <b>null</b> 677 */ 678 @Override 679 public Configuration getSource(String key) 680 { 681 if (key == null) 682 { 683 throw new IllegalArgumentException("Key must not be null!"); 684 } 685 return getCurrentConfig().getSource(key); 686 } 687 688 @Override 689 public void addConfigurationListener(ConfigurationListener l) 690 { 691 super.addConfigurationListener(l); 692 693 for (CombinedConfiguration cc : configs.values()) 694 { 695 cc.addConfigurationListener(l); 696 } 697 } 698 699 @Override 700 public boolean removeConfigurationListener(ConfigurationListener l) 701 { 702 for (CombinedConfiguration cc : configs.values()) 703 { 704 cc.removeConfigurationListener(l); 705 } 706 return super.removeConfigurationListener(l); 707 } 708 709 @Override 710 public Collection<ConfigurationListener> getConfigurationListeners() 711 { 712 return super.getConfigurationListeners(); 713 } 714 715 @Override 716 public void clearConfigurationListeners() 717 { 718 for (CombinedConfiguration cc : configs.values()) 719 { 720 cc.clearConfigurationListeners(); 721 } 722 super.clearConfigurationListeners(); 723 } 724 725 @Override 726 public void addErrorListener(ConfigurationErrorListener l) 727 { 728 for (CombinedConfiguration cc : configs.values()) 729 { 730 cc.addErrorListener(l); 731 } 732 super.addErrorListener(l); 733 } 734 735 @Override 736 public boolean removeErrorListener(ConfigurationErrorListener l) 737 { 738 for (CombinedConfiguration cc : configs.values()) 739 { 740 cc.removeErrorListener(l); 741 } 742 return super.removeErrorListener(l); 743 } 744 745 @Override 746 public void clearErrorListeners() 747 { 748 for (CombinedConfiguration cc : configs.values()) 749 { 750 cc.clearErrorListeners(); 751 } 752 super.clearErrorListeners(); 753 } 754 755 @Override 756 public Collection<ConfigurationErrorListener> getErrorListeners() 757 { 758 return super.getErrorListeners(); 759 } 760 761 /** 762 * Returns a copy of this object. This implementation performs a deep clone, 763 * i.e. all contained configurations will be cloned, too. For this to work, 764 * all contained configurations must be cloneable. Registered event 765 * listeners won't be cloned. The clone will use the same node combiner than 766 * the original. 767 * 768 * @return the copied object 769 */ 770 @Override 771 public Object clone() 772 { 773 return super.clone(); 774 } 775 776 /** 777 * Invalidates the current combined configuration. This means that the next time a 778 * property is accessed the combined node structure must be re-constructed. 779 * Invalidation of a combined configuration also means that an event of type 780 * {@code EVENT_COMBINED_INVALIDATE} is fired. Note that while other 781 * events most times appear twice (once before and once after an update), 782 * this event is only fired once (after update). 783 */ 784 @Override 785 public void invalidate() 786 { 787 getCurrentConfig().invalidate(); 788 } 789 790 public void invalidateAll() 791 { 792 if (configs == null) 793 { 794 return; 795 } 796 for (CombinedConfiguration cc : configs.values()) 797 { 798 cc.invalidate(); 799 } 800 } 801 802 /* 803 * Don't allow resolveContainerStore to be called recursively. 804 * @param key The key to resolve. 805 * @return The value of the key. 806 */ 807 @Override 808 protected Object resolveContainerStore(String key) 809 { 810 if (recursive.get().booleanValue()) 811 { 812 return null; 813 } 814 recursive.set(Boolean.TRUE); 815 try 816 { 817 return super.resolveContainerStore(key); 818 } 819 finally 820 { 821 recursive.set(Boolean.FALSE); 822 } 823 } 824 825 private CombinedConfiguration getCurrentConfig() 826 { 827 String key = localSubst.replace(keyPattern); 828 CombinedConfiguration config = configs.get(key); 829 // The double-checked works here due to the Thread guarantees of ConcurrentMap. 830 if (config == null) 831 { 832 synchronized (configs) 833 { 834 config = configs.get(key); 835 if (config == null) 836 { 837 config = new CombinedConfiguration(getNodeCombiner()); 838 if (loggerName != null) 839 { 840 Log log = LogFactory.getLog(loggerName); 841 if (log != null) 842 { 843 config.setLogger(log); 844 } 845 } 846 config.setIgnoreReloadExceptions(isIgnoreReloadExceptions()); 847 config.setExpressionEngine(this.getExpressionEngine()); 848 config.setDelimiterParsingDisabled(isDelimiterParsingDisabled()); 849 config.setConversionExpressionEngine(getConversionExpressionEngine()); 850 config.setListDelimiter(getListDelimiter()); 851 for (ConfigurationErrorListener listener : getErrorListeners()) 852 { 853 config.addErrorListener(listener); 854 } 855 for (ConfigurationListener listener : getConfigurationListeners()) 856 { 857 config.addConfigurationListener(listener); 858 } 859 config.setForceReloadCheck(isForceReloadCheck()); 860 for (ConfigData data : configurations) 861 { 862 config.addConfiguration(data.getConfiguration(), data.getName(), data.getAt()); 863 } 864 configs.put(key, config); 865 } 866 } 867 } 868 if (getLogger().isDebugEnabled()) 869 { 870 getLogger().debug("Returning config for " + key + ": " + config); 871 } 872 return config; 873 } 874 875 /** 876 * Internal class that identifies each Configuration. 877 */ 878 static class ConfigData 879 { 880 /** Stores a reference to the configuration. */ 881 private AbstractConfiguration configuration; 882 883 /** Stores the name under which the configuration is stored. */ 884 private String name; 885 886 /** Stores the at string.*/ 887 private String at; 888 889 /** 890 * Creates a new instance of {@code ConfigData} and initializes 891 * it. 892 * 893 * @param config the configuration 894 * @param n the name 895 * @param at the at position 896 */ 897 public ConfigData(AbstractConfiguration config, String n, String at) 898 { 899 configuration = config; 900 name = n; 901 this.at = at; 902 } 903 904 /** 905 * Returns the stored configuration. 906 * 907 * @return the configuration 908 */ 909 public AbstractConfiguration getConfiguration() 910 { 911 return configuration; 912 } 913 914 /** 915 * Returns the configuration's name. 916 * 917 * @return the name 918 */ 919 public String getName() 920 { 921 return name; 922 } 923 924 /** 925 * Returns the at position of this configuration. 926 * 927 * @return the at position 928 */ 929 public String getAt() 930 { 931 return at; 932 } 933 934 } 935}