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}