View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration;
18  
19  import java.math.BigDecimal;
20  import java.math.BigInteger;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Properties;
28  import java.util.Set;
29  import java.util.concurrent.ConcurrentHashMap;
30  import java.util.concurrent.ConcurrentMap;
31  
32  import org.apache.commons.configuration.event.ConfigurationErrorListener;
33  import org.apache.commons.configuration.event.ConfigurationListener;
34  import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
35  import org.apache.commons.configuration.tree.ConfigurationNode;
36  import org.apache.commons.configuration.tree.ExpressionEngine;
37  import org.apache.commons.configuration.tree.NodeCombiner;
38  import org.apache.commons.lang.text.StrSubstitutor;
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  
42  /**
43   * DynamicCombinedConfiguration allows a set of CombinedConfigurations to be used. Each CombinedConfiguration
44   * is referenced by a key that is dynamically constructed from a key pattern on each call. The key pattern
45   * will be resolved using the configured ConfigurationInterpolator.
46   * @since 1.6
47   * @author <a
48   * href="http://commons.apache.org/configuration/team-list.html">Commons
49   * Configuration team</a>
50   * @version $Id: DynamicCombinedConfiguration.java 1367234 2012-07-30 19:32:00Z oheger $
51   */
52  public class DynamicCombinedConfiguration extends CombinedConfiguration
53  {
54      /**
55       * Prevent recursion while resolving unprefixed properties.
56       */
57      private static ThreadLocal<Boolean> recursive = new ThreadLocal<Boolean>()
58      {
59          @Override
60          protected synchronized Boolean initialValue()
61          {
62              return Boolean.FALSE;
63          }
64      };
65  
66      /** The CombinedConfigurations */
67      private final ConcurrentMap<String, CombinedConfiguration> configs =
68              new ConcurrentHashMap<String, CombinedConfiguration>();
69  
70      /** Stores a list with the contained configurations. */
71      private List<ConfigData> configurations = new ArrayList<ConfigData>();
72  
73      /** Stores a map with the named configurations. */
74      private Map<String, AbstractConfiguration> namedConfigurations =
75              new HashMap<String, AbstractConfiguration>();
76  
77      /** The key pattern for the CombinedConfiguration map */
78      private String keyPattern;
79  
80      /** Stores the combiner. */
81      private NodeCombiner nodeCombiner;
82  
83      /** The name of the logger to use for each CombinedConfiguration */
84      private String loggerName = DynamicCombinedConfiguration.class.getName();
85  
86      /** The object for handling variable substitution in key patterns. */
87      private StrSubstitutor localSubst = new StrSubstitutor(new ConfigurationInterpolator());
88  
89      /**
90       * Creates a new instance of {@code DynamicCombinedConfiguration} and
91       * initializes the combiner to be used.
92       *
93       * @param comb the node combiner (can be <b>null</b>, then a union combiner
94       * is used as default)
95       */
96      public DynamicCombinedConfiguration(NodeCombiner comb)
97      {
98          super();
99          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<Object> 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 }