SubnodeConfiguration.java

  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.configuration2;

  18. import org.apache.commons.configuration2.tree.ImmutableNode;
  19. import org.apache.commons.configuration2.tree.InMemoryNodeModel;
  20. import org.apache.commons.configuration2.tree.InMemoryNodeModelSupport;
  21. import org.apache.commons.configuration2.tree.NodeModel;
  22. import org.apache.commons.configuration2.tree.NodeSelector;
  23. import org.apache.commons.configuration2.tree.TrackedNodeModel;

  24. /**
  25.  * <p>
  26.  * A specialized hierarchical configuration class with a node model that uses a tracked node of another node model as
  27.  * its root node.
  28.  * </p>
  29.  * <p>
  30.  * Configurations of this type are initialized with a special {@link NodeModel} operating on a specific tracked node of
  31.  * the parent configuration and the corresponding {@link NodeSelector}. All property accessor methods are evaluated
  32.  * relative to this root node. A good use case for a {@code SubnodeConfiguration} is when multiple properties from a
  33.  * specific sub tree of the whole configuration need to be accessed. Then a {@code SubnodeConfiguration} can be created
  34.  * with the parent node of the affected sub tree as root node. This allows for simpler property keys and is also more
  35.  * efficient.
  36.  * </p>
  37.  * <p>
  38.  * By making use of a tracked node as root node, a {@code SubnodeConfiguration} and its parent configuration initially
  39.  * operate on the same hierarchy of configuration nodes. So if modifications are performed at the subnode configuration,
  40.  * these changes are immediately visible in the parent configuration. Analogously will updates of the parent
  41.  * configuration affect the {@code SubnodeConfiguration} if the sub tree spanned by the {@code SubnodeConfiguration}'s
  42.  * root node is involved.
  43.  * </p>
  44.  * <p>
  45.  * Note that by making use of a {@code NodeSelector} the {@code SubnodeConfiguration} is not associated with a physical
  46.  * node instance, but the selection criteria stored in the selector are evaluated after each change of the nodes
  47.  * structure. As an example consider that the selector uses a key with an index into a list element, say index 2. Now if
  48.  * an update occurs on the underlying nodes structure which removes the first element in this list structure, the
  49.  * {@code SubnodeConfiguration} still references the element with index 2 which is now another one.
  50.  * </p>
  51.  * <p>
  52.  * There are also possible changes of the underlying nodes structure which completely detach the
  53.  * {@code SubnodeConfiguration} from its parent configuration. For instance, the key referenced by the
  54.  * {@code SubnodeConfiguration} could be removed in the parent configuration. If this happens, the
  55.  * {@code SubnodeConfiguration} stays functional; however, it now operates on a separate node model than its parent
  56.  * configuration. Changes made by one configuration are no longer visible for the other one (as the node models have no
  57.  * longer overlapping nodes, there is no way to have a synchronization here).
  58.  * </p>
  59.  * <p>
  60.  * When a subnode configuration is created, it inherits the settings of its parent configuration, for example some flags like
  61.  * the {@code throwExceptionOnMissing} flag or the settings for handling list delimiters) or the expression engine. If
  62.  * these settings are changed later in either the subnode or the parent configuration, the changes are not visible for
  63.  * each other. So you could create a subnode configuration, and change its expression engine without affecting the
  64.  * parent configuration.
  65.  * </p>
  66.  * <p>
  67.  * Because the {@code SubnodeConfiguration} operates on the same nodes structure as its parent it uses the same
  68.  * {@code Synchronizer} instance per default. This means that locks held on one {@code SubnodeConfiguration} also impact
  69.  * the parent configuration and all of its other {@code SubnodeConfiguration} objects. You should not change this
  70.  * without a good reason! Otherwise, there is the risk of data corruption when multiple threads access these
  71.  * configuration concurrently.
  72.  * </p>
  73.  * <p>
  74.  * From its purpose this class is quite similar to {@link SubsetConfiguration}. The difference is that a subset
  75.  * configuration of a hierarchical configuration may combine multiple configuration nodes from different sub trees of
  76.  * the configuration, while all nodes in a subnode configuration belong to the same sub tree. If an application can live
  77.  * with this limitation, it is recommended to use this class instead of {@code SubsetConfiguration} because creating a
  78.  * subset configuration is more expensive than creating a subnode configuration.
  79.  * </p>
  80.  * <p>
  81.  * It is strongly recommended to create {@code SubnodeConfiguration} instances only through the
  82.  * {@code configurationAt()} methods of a hierarchical configuration. These methods ensure that all necessary
  83.  * initializations are done. Creating instances manually without doing proper initialization may break some of the
  84.  * functionality provided by this class.
  85.  * </p>
  86.  *
  87.  * @since 1.3
  88.  */
  89. public class SubnodeConfiguration extends BaseHierarchicalConfiguration {
  90.     /** Stores the parent configuration. */
  91.     private final BaseHierarchicalConfiguration parent;

  92.     /** The node selector selecting the root node of this configuration. */
  93.     private final NodeSelector rootSelector;

  94.     /**
  95.      * Creates a new instance of {@code SubnodeConfiguration} and initializes it with all relevant properties.
  96.      *
  97.      * @param parent the parent configuration
  98.      * @param model the {@code TrackedNodeModel} to be used for this configuration
  99.      * @throws IllegalArgumentException if a required argument is missing
  100.      */
  101.     public SubnodeConfiguration(final BaseHierarchicalConfiguration parent, final TrackedNodeModel model) {
  102.         super(model);
  103.         if (parent == null) {
  104.             throw new IllegalArgumentException("Parent configuration must not be null!");
  105.         }
  106.         if (model == null) {
  107.             throw new IllegalArgumentException("Node model must not be null!");
  108.         }

  109.         this.parent = parent;
  110.         rootSelector = model.getSelector();
  111.     }

  112.     /**
  113.      * {@inheritDoc} This implementation returns a copy of the current node model with the same settings. However, it has to
  114.      * be ensured that the track count for the node selector is increased.
  115.      *
  116.      * @return the node model for the clone
  117.      */
  118.     @Override
  119.     protected NodeModel<ImmutableNode> cloneNodeModel() {
  120.         final InMemoryNodeModel parentModel = (InMemoryNodeModel) getParent().getModel();
  121.         parentModel.trackNode(getRootSelector(), getParent());
  122.         return new TrackedNodeModel(getParent(), getRootSelector(), true);
  123.     }

  124.     /**
  125.      * Closes this sub configuration. This method closes the underlying {@link TrackedNodeModel}, thus causing the tracked
  126.      * node acting as root node to be released. Per default, this happens automatically when the model is claimed by the
  127.      * garbage collector. By calling this method explicitly, it can be indicated that this configuration is no longer used
  128.      * and that resources used by it can be freed immediately.
  129.      */
  130.     public void close() {
  131.         getTrackedModel().close();
  132.     }

  133.     /**
  134.      * {@inheritDoc} This implementation makes sure that the correct node model (the one of the parent) is used for the new
  135.      * sub configuration.
  136.      */
  137.     @Override
  138.     protected SubnodeConfiguration createSubConfigurationForTrackedNode(final NodeSelector selector, final InMemoryNodeModelSupport parentModelSupport) {
  139.         return super.createSubConfigurationForTrackedNode(selector, getParent());
  140.     }

  141.     /**
  142.      * {@inheritDoc} This implementation returns a newly created node model with the correct root node set. Note that this
  143.      * model is not used for property access, but only made available to clients that need to operate on the node structure
  144.      * of this {@code SubnodeConfiguration}. Be aware that the implementation of this method is not very efficient.
  145.      */
  146.     @Override
  147.     public InMemoryNodeModel getNodeModel() {
  148.         final ImmutableNode root = getParent().getNodeModel().getTrackedNode(getRootSelector());
  149.         return new InMemoryNodeModel(root);
  150.     }

  151.     /**
  152.      * Gets the parent configuration of this subnode configuration.
  153.      *
  154.      * @return the parent configuration
  155.      */
  156.     public BaseHierarchicalConfiguration getParent() {
  157.         return parent;
  158.     }

  159.     /**
  160.      * Gets the node model of the root configuration. {@code SubnodeConfiguration} instances created from a hierarchical
  161.      * configuration operate on the same node model, using different nodes as their local root nodes. With this method the
  162.      * top-level node model can be obtained. It works even in constellations where a {@code SubnodeConfiguration} has been
  163.      * created from another {@code SubnodeConfiguration}.
  164.      *
  165.      * @return the root node model
  166.      * @since 2.2
  167.      */
  168.     public InMemoryNodeModel getRootNodeModel() {
  169.         if (getParent() instanceof SubnodeConfiguration) {
  170.             return ((SubnodeConfiguration) getParent()).getRootNodeModel();
  171.         }
  172.         return getParent().getNodeModel();
  173.     }

  174.     /**
  175.      * Gets the selector to the root node of this configuration.
  176.      *
  177.      * @return the {@code NodeSelector} to the root node
  178.      */
  179.     public NodeSelector getRootSelector() {
  180.         return rootSelector;
  181.     }

  182.     /**
  183.      * {@inheritDoc} This implementation returns a sub selector of the selector of this configuration.
  184.      */
  185.     @Override
  186.     protected NodeSelector getSubConfigurationNodeSelector(final String key) {
  187.         return getRootSelector().subSelector(key);
  188.     }

  189.     /**
  190.      * {@inheritDoc} This implementation returns the parent model of the {@link TrackedNodeModel} used by this
  191.      * configuration.
  192.      */
  193.     @Override
  194.     protected InMemoryNodeModel getSubConfigurationParentModel() {
  195.         return getTrackedModel().getParentModel();
  196.     }

  197.     /**
  198.      * Convenience method that returns the tracked model used by this sub configuration.
  199.      *
  200.      * @return the {@code TrackedNodeModel}
  201.      */
  202.     private TrackedNodeModel getTrackedModel() {
  203.         return (TrackedNodeModel) getModel();
  204.     }
  205. }