    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}