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 */
017package org.apache.commons.configuration2.reloading;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections;
022
023/**
024 * <p>
025 * A specialized {@code ReloadingController} implementation which manages an
026 * arbitrary number of other {@code ReloadingController} objects.
027 * </p>
028 * <p>
029 * This class can be used to handle multiple simple controllers for reload
030 * operations as a single object. As a usage example consider a combined
031 * configuration containing a number of configuration sources of which some
032 * support reloading. In this scenario all {@code ReloadingController} instances
033 * for the reloading-enabled sources can be added to a
034 * {@code CombinedReloadingController}. Then by triggering the combined
035 * controller a reload check is performed on all child sources.
036 * </p>
037 * <p>
038 * This class is a typical implementation of the <em>composite pattern</em>. An
039 * instance is constructed with a collection of sub {@code ReloadingController}
040 * objects. Its operations are implemented by delegating to all child
041 * controllers.
042 * </p>
043 * <p>
044 * This class expects the managed controller objects to be passed to the
045 * constructor. From this list a defensive copy is created so that it cannot be
046 * changed later on. Derived classes can override the
047 * {@link #getSubControllers()} method if they need another way to handle child
048 * controllers (e.g. a more dynamic way). However, they are then responsible to
049 * ensure a safe access to this list in a multi-threaded environment.
050 * </p>
051 *
052 * @version $Id: CombinedReloadingController.java 1842194 2018-09-27 22:24:23Z ggregory $
053 * @since 2.0
054 */
055public class CombinedReloadingController extends ReloadingController
056{
057    /** Constant for a dummy reloading detector. */
058    private static final ReloadingDetector DUMMY =
059            new MultiReloadingControllerDetector(null);
060
061    /** The collection with managed reloading controllers. */
062    private final Collection<ReloadingController> controllers;
063
064    /** The reloading detector used by this instance. */
065    private final ReloadingDetector detector;
066
067    /**
068     * Creates a new instance of {@code CombinedReloadingController} and
069     * initializes it with the {@code ReloadingController} objects to be
070     * managed.
071     *
072     * @param subCtrls the collection with sub {@code ReloadingController}s
073     *        (must not be <b>null</b> or contain <b>null</b> entries)
074     * @throws IllegalArgumentException if the passed in collection is
075     *         <b>null</b> or contains <b>null</b> entries
076     */
077    public CombinedReloadingController(
078            final Collection<? extends ReloadingController> subCtrls)
079    {
080        super(DUMMY);
081        controllers = checkManagedControllers(subCtrls);
082        detector = new MultiReloadingControllerDetector(this);
083    }
084
085    /**
086     * Returns a (unmodifiable) collection with the sub controllers managed by
087     * this combined controller.
088     *
089     * @return a collection with sub controllers
090     */
091    public Collection<ReloadingController> getSubControllers()
092    {
093        return controllers;
094    }
095
096    /**
097     * {@inheritDoc} This implementation returns a special reloading detector
098     * which operates on all managed controllers.
099     */
100    @Override
101    public ReloadingDetector getDetector()
102    {
103        return detector;
104    }
105
106    /**
107     * Resets the reloading state of all managed sub controllers
108     * unconditionally. This method is intended to be called after the creation
109     * of an instance. It may be the case that some of the sub controllers are
110     * already in reloading state, so their state is out of sync with this
111     * controller's global reloading state. This method ensures that the
112     * reloading state of all sub controllers is reset.
113     */
114    public void resetInitialReloadingState()
115    {
116        getDetector().reloadingPerformed();
117    }
118
119    /**
120     * Checks the collection with the passed in sub controllers and creates a
121     * defensive copy.
122     *
123     * @param subCtrls the collection with sub controllers
124     * @return a copy of the collection to be stored in the newly created
125     *         instance
126     * @throws IllegalArgumentException if the passed in collection is
127     *         <b>null</b> or contains <b>null</b> entries
128     */
129    private static Collection<ReloadingController> checkManagedControllers(
130            final Collection<? extends ReloadingController> subCtrls)
131    {
132        if (subCtrls == null)
133        {
134            throw new IllegalArgumentException(
135                    "Collection with sub controllers must not be null!");
136        }
137        final Collection<ReloadingController> ctrls =
138                new ArrayList<>(subCtrls);
139        for (final ReloadingController rc : ctrls)
140        {
141            if (rc == null)
142            {
143                throw new IllegalArgumentException(
144                        "Collection with sub controllers contains a null entry!");
145            }
146        }
147
148        return Collections.unmodifiableCollection(ctrls);
149    }
150
151    /**
152     * A specialized implementation of the {@code ReloadingDetector} interface
153     * which operates on a collection of {@code ReloadingController} objects.
154     * The methods defined by the {@code ReloadingDetector} interface are
155     * delegated to the managed controllers.
156     */
157    private static class MultiReloadingControllerDetector implements
158            ReloadingDetector
159    {
160        /** A reference to the owning combined reloading controller. */
161        private final CombinedReloadingController owner;
162
163        /**
164         * Creates a new instance of {@code MultiReloadingControllerDetector}.
165         *
166         * @param o the owner
167         */
168        public MultiReloadingControllerDetector(final CombinedReloadingController o)
169        {
170            owner = o;
171        }
172
173        /**
174         * {@inheritDoc} This implementation delegates to the managed
175         * controllers. For all of them the {@code checkForReloading()}
176         * method is called, giving them the chance to trigger a reload if
177         * necessary. If one of these calls returns <b>true</b>, the result of
178         * this method is <b>true</b>, otherwise <b>false</b>.
179         */
180        @Override
181        public boolean isReloadingRequired()
182        {
183            boolean result = false;
184            for (final ReloadingController rc : owner.getSubControllers())
185            {
186                if (rc.checkForReloading(null))
187                {
188                    result = true;
189                }
190            }
191            return result;
192        }
193
194        /**
195         * {@inheritDoc} This implementation resets the reloading state on all
196         * managed controllers.
197         */
198        @Override
199        public void reloadingPerformed()
200        {
201            for (final ReloadingController rc : owner.getSubControllers())
202            {
203                rc.resetReloadingState();
204            }
205        }
206    }
207}