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