    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}