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 */
017 package org.apache.commons.configuration;
018
019 import java.math.BigDecimal;
020 import java.math.BigInteger;
021 import java.util.ArrayList;
022 import java.util.Collection;
023 import java.util.HashMap;
024 import java.util.Iterator;
025 import java.util.List;
026 import java.util.Map;
027 import java.util.Properties;
028 import java.util.Set;
029 import java.util.concurrent.ConcurrentHashMap;
030 import java.util.concurrent.ConcurrentMap;
031
032 import org.apache.commons.configuration.event.ConfigurationErrorListener;
033 import org.apache.commons.configuration.event.ConfigurationListener;
034 import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
035 import org.apache.commons.configuration.tree.ConfigurationNode;
036 import org.apache.commons.configuration.tree.ExpressionEngine;
037 import org.apache.commons.configuration.tree.NodeCombiner;
038 import org.apache.commons.lang.text.StrSubstitutor;
039 import org.apache.commons.logging.Log;
040 import 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 1367234 2012-07-30 19:32:00Z oheger $
051 */
052 public 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<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 }