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