TrackedNodeModel.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.tree;

  18. import java.util.Collection;
  19. import java.util.List;
  20. import java.util.concurrent.atomic.AtomicBoolean;

  21. /**
  22.  * <p>
  23.  * A specialized {@code NodeModel} implementation that uses a tracked node managed by an {@link InMemoryNodeModel}
  24.  * object as root node.
  25.  * </p>
  26.  * <p>
  27.  * Models of this type are useful when working on specific sub trees of a nodes structure. This is the case for instance
  28.  * for a {@code SubnodeConfiguration}.
  29.  * </p>
  30.  * <p>
  31.  * An instance of this class is constructed with an {@link InMemoryNodeModelSupport} object providing a reference to the
  32.  * underlying {@code InMemoryNodeModel} and the {@link NodeSelector} pointing to the tracked node acting as this model's
  33.  * root node. The {@code NodeModel} operations are implemented by delegating to the wrapped {@code InMemoryNodeModel}
  34.  * object specifying the selector to the tracked node as target root node for the update transaction. Note that the
  35.  * tracked node can become detached at any time. This situation is handled transparently by the implementation of
  36.  * {@code InMemoryNodeModel}. The reason for using an {@code InMemoryNodeModelSupport} object rather than an
  37.  * {@code InMemoryNodeModel} directly is that this additional layer of indirection can be used for performing special
  38.  * initializations on the model before it is returned to the {@code TrackedNodeModel} object. This is needed by some
  39.  * dynamic configuration implementations, for example by {@code CombinedConfiguration}.
  40.  * </p>
  41.  * <p>
  42.  * If the tracked node acting as root node is exclusively used by this model, it should be released when this model is
  43.  * no longer needed. This can be done manually by calling the {@link #close()} method. It is also possible to pass a
  44.  * value of <strong>true</strong> to the {@code untrackOnFinalize} argument of the constructor. This causes
  45.  * {@code close()} to be called automatically if this object gets claimed by the garbage collector.
  46.  * </p>
  47.  * <p>
  48.  * As {@code InMemoryNodeModel}, this class is thread-safe.
  49.  * </p>
  50.  *
  51.  * @since 2.0
  52.  */
  53. public class TrackedNodeModel implements NodeModel<ImmutableNode> {
  54.     /** Stores the underlying parent model. */
  55.     private final InMemoryNodeModelSupport parentModelSupport;

  56.     /** The selector for the managed tracked node. */
  57.     private final NodeSelector selector;

  58.     /**
  59.      * A flag whether the tracked not should be released when this object is finalized.
  60.      */
  61.     private final boolean releaseTrackedNodeOnFinalize;

  62.     /** A flag whether this model has already been closed. */
  63.     private final AtomicBoolean closed;

  64.     /**
  65.      * Creates a new instance of {@code TrackedNodeModel} and initializes it with the given underlying model and the
  66.      * selector to the root node. The boolean argument controls whether the associated tracked node should be released when
  67.      * this object gets finalized. This allows the underlying model to free some resources. If used as model within a
  68.      * {@code SubnodeConfiguration}, there is typically no way to discard the model explicitly. Therefore, it makes sense to
  69.      * do this automatically on finalization.
  70.      *
  71.      * @param modelSupport the underlying {@code InMemoryNodeModelSupport} (must not be <strong>null</strong>)
  72.      * @param sel the selector to the root node of this model (must not be <strong>null</strong>)
  73.      * @param untrackOnFinalize a flag whether the tracked node should be released on finalization
  74.      * @throws IllegalArgumentException if a required parameter is missing
  75.      */
  76.     public TrackedNodeModel(final InMemoryNodeModelSupport modelSupport, final NodeSelector sel, final boolean untrackOnFinalize) {
  77.         if (modelSupport == null) {
  78.             throw new IllegalArgumentException("Underlying model support must not be null!");
  79.         }
  80.         if (sel == null) {
  81.             throw new IllegalArgumentException("Selector must not be null!");
  82.         }

  83.         parentModelSupport = modelSupport;
  84.         selector = sel;
  85.         releaseTrackedNodeOnFinalize = untrackOnFinalize;
  86.         closed = new AtomicBoolean();
  87.     }

  88.     @Override
  89.     public void addNodes(final String key, final Collection<? extends ImmutableNode> nodes, final NodeKeyResolver<ImmutableNode> resolver) {
  90.         getParentModel().addNodes(key, getSelector(), nodes, resolver);
  91.     }

  92.     @Override
  93.     public void addProperty(final String key, final Iterable<?> values, final NodeKeyResolver<ImmutableNode> resolver) {
  94.         getParentModel().addProperty(key, getSelector(), values, resolver);
  95.     }

  96.     /**
  97.      * {@inheritDoc} This implementation clears the sub tree spanned by the associate tracked node. This has the side effect
  98.      * that this in any case becomes detached.
  99.      *
  100.      * @param resolver the {@code NodeKeyResolver}.
  101.      */
  102.     @Override
  103.     public void clear(final NodeKeyResolver<ImmutableNode> resolver) {
  104.         getParentModel().clearTree(null, getSelector(), resolver);
  105.     }

  106.     @Override
  107.     public void clearProperty(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
  108.         getParentModel().clearProperty(key, getSelector(), resolver);
  109.     }

  110.     @Override
  111.     public List<QueryResult<ImmutableNode>> clearTree(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
  112.         return getParentModel().clearTree(key, getSelector(), resolver);
  113.     }

  114.     /**
  115.      * Closes this model. This causes the tracked node this model is based upon to be released (i.e.
  116.      * {@link InMemoryNodeModel#untrackNode(NodeSelector)} is called). This method should be called when this model is no
  117.      * longer needed. This implementation is idempotent; it is safe to call {@code close()} multiple times - only the first
  118.      * invocation has an effect. After this method has been called this model can no longer be used because there is no
  119.      * guarantee that the node can still be accessed from the parent model.
  120.      */
  121.     public void close() {
  122.         if (closed.compareAndSet(false, true)) {
  123.             getParentModel().untrackNode(getSelector());
  124.         }
  125.     }

  126.     /**
  127.      * {@inheritDoc} This implementation calls {@code close()} if the {@code untrackOnFinalize} flag was set when this
  128.      * instance was constructed. While this is not 100 percent reliable, it is better than keeping the tracked node hanging
  129.      * around. Note that it is not a problem if {@code close()} already had been invoked manually because this method is
  130.      * idempotent.
  131.      *
  132.      * @see #close()
  133.      */
  134.     @Override
  135.     protected void finalize() throws Throwable {
  136.         if (isReleaseTrackedNodeOnFinalize()) {
  137.             close();
  138.         }
  139.         super.finalize();
  140.     }

  141.     /**
  142.      * {@inheritDoc} This implementation returns the tracked node instance acting as root node of this model.
  143.      */
  144.     @Override
  145.     public ImmutableNode getInMemoryRepresentation() {
  146.         return getNodeHandler().getRootNode();
  147.     }

  148.     @Override
  149.     public NodeHandler<ImmutableNode> getNodeHandler() {
  150.         return getParentModel().getTrackedNodeHandler(getSelector());
  151.     }

  152.     /**
  153.      * Gets the parent model. Operations on this model are delegated to this parent model specifying the selector to the
  154.      * tracked node.
  155.      *
  156.      * @return the parent model
  157.      */
  158.     public InMemoryNodeModel getParentModel() {
  159.         return getParentModelSupport().getNodeModel();
  160.     }

  161.     /**
  162.      * Gets the {@code InMemoryNodeModelSupport} object which is used to gain access to the underlying node model.
  163.      *
  164.      * @return the associated {@code InMemoryNodeModelSupport} object
  165.      */
  166.     public InMemoryNodeModelSupport getParentModelSupport() {
  167.         return parentModelSupport;
  168.     }

  169.     /**
  170.      * Gets the {@code NodeSelector} pointing to the tracked node managed by this model.
  171.      *
  172.      * @return the tracked node selector
  173.      */
  174.     public NodeSelector getSelector() {
  175.         return selector;
  176.     }

  177.     /**
  178.      * Returns the flag whether the managed tracked node is to be released when this object gets finalized. This method
  179.      * returns the value of the corresponding flag passed to the constructor. If result is true, the underlying model is
  180.      * asked to untrack the managed node when this object is claimed by the GC.
  181.      *
  182.      * @return a flag whether the managed tracked node should be released when this object dies
  183.      * @see InMemoryNodeModel#untrackNode(NodeSelector)
  184.      */
  185.     public boolean isReleaseTrackedNodeOnFinalize() {
  186.         return releaseTrackedNodeOnFinalize;
  187.     }

  188.     @Override
  189.     public void setProperty(final String key, final Object value, final NodeKeyResolver<ImmutableNode> resolver) {
  190.         getParentModel().setProperty(key, getSelector(), value, resolver);
  191.     }

  192.     @Override
  193.     public void setRootNode(final ImmutableNode newRoot) {
  194.         getParentModel().replaceTrackedNode(getSelector(), newRoot);
  195.     }
  196. }