    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}