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 19 import org.apache.commons.configuration2.tree.ImmutableNode; 20 import org.apache.commons.configuration2.tree.InMemoryNodeModel; 21 import org.apache.commons.configuration2.tree.InMemoryNodeModelSupport; 22 import org.apache.commons.configuration2.tree.NodeModel; 23 import org.apache.commons.configuration2.tree.NodeSelector; 24 import org.apache.commons.configuration2.tree.TrackedNodeModel; 25 26 /** 27 * <p> 28 * A specialized hierarchical configuration class with a node model that uses a tracked node of another node model as 29 * its root node. 30 * </p> 31 * <p> 32 * Configurations of this type are initialized with a special {@link NodeModel} operating on a specific tracked node of 33 * the parent configuration and the corresponding {@link NodeSelector}. All property accessor methods are evaluated 34 * relative to this root node. A good use case for a {@code SubnodeConfiguration} is when multiple properties from a 35 * specific sub tree of the whole configuration need to be accessed. Then a {@code SubnodeConfiguration} can be created 36 * with the parent node of the affected sub tree as root node. This allows for simpler property keys and is also more 37 * efficient. 38 * </p> 39 * <p> 40 * By making use of a tracked node as root node, a {@code SubnodeConfiguration} and its parent configuration initially 41 * operate on the same hierarchy of configuration nodes. So if modifications are performed at the subnode configuration, 42 * these changes are immediately visible in the parent configuration. Analogously will updates of the parent 43 * configuration affect the {@code SubnodeConfiguration} if the sub tree spanned by the {@code SubnodeConfiguration}'s 44 * root node is involved. 45 * </p> 46 * <p> 47 * Note that by making use of a {@code NodeSelector} the {@code SubnodeConfiguration} is not associated with a physical 48 * node instance, but the selection criteria stored in the selector are evaluated after each change of the nodes 49 * structure. As an example consider that the selector uses a key with an index into a list element, say index 2. Now if 50 * an update occurs on the underlying nodes structure which removes the first element in this list structure, the 51 * {@code SubnodeConfiguration} still references the element with index 2 which is now another one. 52 * </p> 53 * <p> 54 * There are also possible changes of the underlying nodes structure which completely detach the 55 * {@code SubnodeConfiguration} from its parent configuration. For instance, the key referenced by the 56 * {@code SubnodeConfiguration} could be removed in the parent configuration. If this happens, the 57 * {@code SubnodeConfiguration} stays functional; however, it now operates on a separate node model than its parent 58 * configuration. Changes made by one configuration are no longer visible for the other one (as the node models have no 59 * longer overlapping nodes, there is no way to have a synchronization here). 60 * </p> 61 * <p> 62 * When a subnode configuration is created, it inherits the settings of its parent configuration, e.g. some flags like 63 * the {@code throwExceptionOnMissing} flag or the settings for handling list delimiters) or the expression engine. If 64 * these settings are changed later in either the subnode or the parent configuration, the changes are not visible for 65 * each other. So you could create a subnode configuration, and change its expression engine without affecting the 66 * parent configuration. 67 * </p> 68 * <p> 69 * Because the {@code SubnodeConfiguration} operates on the same nodes structure as its parent it uses the same 70 * {@code Synchronizer} instance per default. This means that locks held on one {@code SubnodeConfiguration} also impact 71 * the parent configuration and all of its other {@code SubnodeConfiguration} objects. You should not change this 72 * without a good reason! Otherwise, there is the risk of data corruption when multiple threads access these 73 * configuration concurrently. 74 * </p> 75 * <p> 76 * From its purpose this class is quite similar to {@link SubsetConfiguration}. The difference is that a subset 77 * configuration of a hierarchical configuration may combine multiple configuration nodes from different sub trees of 78 * the configuration, while all nodes in a subnode configuration belong to the same sub tree. If an application can live 79 * with this limitation, it is recommended to use this class instead of {@code SubsetConfiguration} because creating a 80 * subset configuration is more expensive than creating a subnode configuration. 81 * </p> 82 * <p> 83 * It is strongly recommended to create {@code SubnodeConfiguration} instances only through the 84 * {@code configurationAt()} methods of a hierarchical configuration. These methods ensure that all necessary 85 * initializations are done. Creating instances manually without doing proper initialization may break some of the 86 * functionality provided by this class. 87 * </p> 88 * 89 * @since 1.3 90 */ 91 public class SubnodeConfiguration extends BaseHierarchicalConfiguration { 92 /** Stores the parent configuration. */ 93 private final BaseHierarchicalConfiguration parent; 94 95 /** The node selector selecting the root node of this configuration. */ 96 private final NodeSelector rootSelector; 97 98 /** 99 * Creates a new instance of {@code SubnodeConfiguration} and initializes it with all relevant properties. 100 * 101 * @param parent the parent configuration 102 * @param model the {@code TrackedNodeModel} to be used for this configuration 103 * @throws IllegalArgumentException if a required argument is missing 104 */ 105 public SubnodeConfiguration(final BaseHierarchicalConfiguration parent, final TrackedNodeModel model) { 106 super(model); 107 if (parent == null) { 108 throw new IllegalArgumentException("Parent configuration must not be null!"); 109 } 110 if (model == null) { 111 throw new IllegalArgumentException("Node model must not be null!"); 112 } 113 114 this.parent = parent; 115 rootSelector = model.getSelector(); 116 } 117 118 /** 119 * Gets the parent configuration of this subnode configuration. 120 * 121 * @return the parent configuration 122 */ 123 public BaseHierarchicalConfiguration getParent() { 124 return parent; 125 } 126 127 /** 128 * Gets the selector to the root node of this configuration. 129 * 130 * @return the {@code NodeSelector} to the root node 131 */ 132 public NodeSelector getRootSelector() { 133 return rootSelector; 134 } 135 136 /** 137 * Closes this sub configuration. This method closes the underlying {@link TrackedNodeModel}, thus causing the tracked 138 * node acting as root node to be released. Per default, this happens automatically when the model is claimed by the 139 * garbage collector. By calling this method explicitly, it can be indicated that this configuration is no longer used 140 * and that resources used by it can be freed immediately. 141 */ 142 public void close() { 143 getTrackedModel().close(); 144 } 145 146 /** 147 * {@inheritDoc} This implementation returns a newly created node model with the correct root node set. Note that this 148 * model is not used for property access, but only made available to clients that need to operate on the node structure 149 * of this {@code SubnodeConfiguration}. Be aware that the implementation of this method is not very efficient. 150 */ 151 @Override 152 public InMemoryNodeModel getNodeModel() { 153 final ImmutableNode root = getParent().getNodeModel().getTrackedNode(getRootSelector()); 154 return new InMemoryNodeModel(root); 155 } 156 157 /** 158 * Gets the node model of the root configuration. {@code SubnodeConfiguration} instances created from a hierarchical 159 * configuration operate on the same node model, using different nodes as their local root nodes. With this method the 160 * top-level node model can be obtained. It works even in constellations where a {@code SubnodeConfiguration} has been 161 * created from another {@code SubnodeConfiguration}. 162 * 163 * @return the root node model 164 * @since 2.2 165 */ 166 public InMemoryNodeModel getRootNodeModel() { 167 if (getParent() instanceof SubnodeConfiguration) { 168 return ((SubnodeConfiguration) getParent()).getRootNodeModel(); 169 } 170 return getParent().getNodeModel(); 171 } 172 173 /** 174 * {@inheritDoc} This implementation returns a copy of the current node model with the same settings. However, it has to 175 * be ensured that the track count for the node selector is increased. 176 * 177 * @return the node model for the clone 178 */ 179 @Override 180 protected NodeModel<ImmutableNode> cloneNodeModel() { 181 final InMemoryNodeModel parentModel = (InMemoryNodeModel) getParent().getModel(); 182 parentModel.trackNode(getRootSelector(), getParent()); 183 return new TrackedNodeModel(getParent(), getRootSelector(), true); 184 } 185 186 /** 187 * {@inheritDoc} This implementation returns a sub selector of the selector of this configuration. 188 */ 189 @Override 190 protected NodeSelector getSubConfigurationNodeSelector(final String key) { 191 return getRootSelector().subSelector(key); 192 } 193 194 /** 195 * {@inheritDoc} This implementation returns the parent model of the {@link TrackedNodeModel} used by this 196 * configuration. 197 */ 198 @Override 199 protected InMemoryNodeModel getSubConfigurationParentModel() { 200 return getTrackedModel().getParentModel(); 201 } 202 203 /** 204 * {@inheritDoc} This implementation makes sure that the correct node model (the one of the parent) is used for the new 205 * sub configuration. 206 */ 207 @Override 208 protected SubnodeConfiguration createSubConfigurationForTrackedNode(final NodeSelector selector, final InMemoryNodeModelSupport parentModelSupport) { 209 return super.createSubConfigurationForTrackedNode(selector, getParent()); 210 } 211 212 /** 213 * Convenience method that returns the tracked model used by this sub configuration. 214 * 215 * @return the {@code TrackedNodeModel} 216 */ 217 private TrackedNodeModel getTrackedModel() { 218 return (TrackedNodeModel) getModel(); 219 } 220 }