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 * @since 2.0
053 */
054public class CombinedReloadingController extends ReloadingController
055{
056    /** Constant for a dummy reloading detector. */
057    private static final ReloadingDetector DUMMY =
058            new MultiReloadingControllerDetector(null);
059
060    /** The collection with managed reloading controllers. */
061    private final Collection<ReloadingController> controllers;
062
063    /** The reloading detector used by this instance. */
064    private final ReloadingDetector detector;
065
066    /**
067     * Creates a new instance of {@code CombinedReloadingController} and
068     * initializes it with the {@code ReloadingController} objects to be
069     * managed.
070     *
071     * @param subCtrls the collection with sub {@code ReloadingController}s
072     *        (must not be <b>null</b> or contain <b>null</b> entries)
073     * @throws IllegalArgumentException if the passed in collection is
074     *         <b>null</b> or contains <b>null</b> entries
075     */
076    public CombinedReloadingController(
077            final Collection<? extends ReloadingController> subCtrls)
078    {
079        super(DUMMY);
080        controllers = checkManagedControllers(subCtrls);
081        detector = new MultiReloadingControllerDetector(this);
082    }
083
084    /**
085     * Returns a (unmodifiable) collection with the sub controllers managed by
086     * this combined controller.
087     *
088     * @return a collection with sub controllers
089     */
090    public Collection<ReloadingController> getSubControllers()
091    {
092        return controllers;
093    }
094
095    /**
096     * {@inheritDoc} This implementation returns a special reloading detector
097     * which operates on all managed controllers.
098     */
099    @Override
100    public ReloadingDetector getDetector()
101    {
102        return detector;
103    }
104
105    /**
106     * Resets the reloading state of all managed sub controllers
107     * unconditionally. This method is intended to be called after the creation
108     * of an instance. It may be the case that some of the sub controllers are
109     * already in reloading state, so their state is out of sync with this
110     * controller's global reloading state. This method ensures that the
111     * reloading state of all sub controllers is reset.
112     */
113    public void resetInitialReloadingState()
114    {
115        getDetector().reloadingPerformed();
116    }
117
118    /**
119     * Checks the collection with the passed in sub controllers and creates a
120     * defensive copy.
121     *
122     * @param subCtrls the collection with sub controllers
123     * @return a copy of the collection to be stored in the newly created
124     *         instance
125     * @throws IllegalArgumentException if the passed in collection is
126     *         <b>null</b> or contains <b>null</b> entries
127     */
128    private static Collection<ReloadingController> checkManagedControllers(
129            final Collection<? extends ReloadingController> subCtrls)
130    {
131        if (subCtrls == null)
132        {
133            throw new IllegalArgumentException(
134                    "Collection with sub controllers must not be null!");
135        }
136        final Collection<ReloadingController> ctrls =
137                new ArrayList<>(subCtrls);
138        for (final ReloadingController rc : ctrls)
139        {
140            if (rc == null)
141            {
142                throw new IllegalArgumentException(
143                        "Collection with sub controllers contains a null entry!");
144            }
145        }
146
147        return Collections.unmodifiableCollection(ctrls);
148    }
149
150    /**
151     * A specialized implementation of the {@code ReloadingDetector} interface
152     * which operates on a collection of {@code ReloadingController} objects.
153     * The methods defined by the {@code ReloadingDetector} interface are
154     * delegated to the managed controllers.
155     */
156    private static class MultiReloadingControllerDetector implements
157            ReloadingDetector
158    {
159        /** A reference to the owning combined reloading controller. */
160        private final CombinedReloadingController owner;
161
162        /**
163         * Creates a new instance of {@code MultiReloadingControllerDetector}.
164         *
165         * @param o the owner
166         */
167        public MultiReloadingControllerDetector(final CombinedReloadingController o)
168        {
169            owner = o;
170        }
171
172        /**
173         * {@inheritDoc} This implementation delegates to the managed
174         * controllers. For all of them the {@code checkForReloading()}
175         * method is called, giving them the chance to trigger a reload if
176         * necessary. If one of these calls returns <b>true</b>, the result of
177         * this method is <b>true</b>, otherwise <b>false</b>.
178         */
179        @Override
180        public boolean isReloadingRequired()
181        {
182            boolean result = false;
183            for (final ReloadingController rc : owner.getSubControllers())
184            {
185                if (rc.checkForReloading(null))
186                {
187                    result = true;
188                }
189            }
190            return result;
191        }
192
193        /**
194         * {@inheritDoc} This implementation resets the reloading state on all
195         * managed controllers.
196         */
197        @Override
198        public void reloadingPerformed()
199        {
200            for (final ReloadingController rc : owner.getSubControllers())
201            {
202                rc.resetReloadingState();
203            }
204        }
205    }
206}