| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.configuration2.reloading; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Objects; |
| |
| /** |
| * <p> |
| * A specialized {@code ReloadingController} implementation which manages an arbitrary number of other |
| * {@code ReloadingController} objects. |
| * </p> |
| * <p> |
| * This class can be used to handle multiple simple controllers for reload operations as a single object. As a usage |
| * example consider a combined configuration containing a number of configuration sources of which some support |
| * reloading. In this scenario all {@code ReloadingController} instances for the reloading-enabled sources can be added |
| * to a {@code CombinedReloadingController}. Then by triggering the combined controller a reload check is performed on |
| * all child sources. |
| * </p> |
| * <p> |
| * This class is a typical implementation of the <em>composite pattern</em>. An instance is constructed with a |
| * collection of sub {@code ReloadingController} objects. Its operations are implemented by delegating to all child |
| * controllers. |
| * </p> |
| * <p> |
| * This class expects the managed controller objects to be passed to the constructor. From this list a defensive copy is |
| * created so that it cannot be changed later on. Derived classes can override the {@link #getSubControllers()} method |
| * if they need another way to handle child controllers (for example a more dynamic way). However, they are then responsible to |
| * ensure a safe access to this list in a multi-threaded environment. |
| * </p> |
| * |
| * @since 2.0 |
| */ |
| public class CombinedReloadingController extends ReloadingController { |
| |
| /** |
| * A specialized implementation of the {@code ReloadingDetector} interface which operates on a collection of |
| * {@code ReloadingController} objects. The methods defined by the {@code ReloadingDetector} interface are delegated to |
| * the managed controllers. |
| */ |
| private static final class MultiReloadingControllerDetector implements ReloadingDetector { |
| |
| /** A reference to the owning combined reloading controller. */ |
| private final CombinedReloadingController owner; |
| |
| /** |
| * Creates a new instance of {@code MultiReloadingControllerDetector}. |
| * |
| * @param owner the owner |
| */ |
| public MultiReloadingControllerDetector(final CombinedReloadingController owner) { |
| this.owner = owner; |
| } |
| |
| /** |
| * {@inheritDoc} This implementation delegates to the managed controllers. For all of them the |
| * {@code checkForReloading()} method is called, giving them the chance to trigger a reload if necessary. If one of |
| * these calls returns <strong>true</strong>, the result of this method is <strong>true</strong>, otherwise <strong>false</strong>. |
| */ |
| @Override |
| public boolean isReloadingRequired() { |
| return owner.getSubControllers().stream().reduce(false, (b, rc) -> b | rc.checkForReloading(null), (t, u) -> t | u); |
| } |
| |
| /** |
| * {@inheritDoc} This implementation resets the reloading state on all managed controllers. |
| */ |
| @Override |
| public void reloadingPerformed() { |
| owner.getSubControllers().forEach(ReloadingController::resetReloadingState); |
| } |
| } |
| |
| /** Constant for a dummy reloading detector. */ |
| private static final ReloadingDetector DUMMY = new MultiReloadingControllerDetector(null); |
| |
| /** |
| * Checks the collection with the passed in sub controllers and creates a defensive copy. |
| * |
| * @param subCtrls the collection with sub controllers |
| * @return a copy of the collection to be stored in the newly created instance |
| * @throws IllegalArgumentException if the passed in collection is <strong>null</strong> or contains <strong>null</strong> entries |
| */ |
| private static Collection<ReloadingController> checkManagedControllers(final Collection<? extends ReloadingController> subCtrls) { |
| if (subCtrls == null) { |
| throw new IllegalArgumentException("Collection with sub controllers must not be null!"); |
| } |
| final Collection<ReloadingController> ctrls = new ArrayList<>(subCtrls); |
| if (ctrls.stream().anyMatch(Objects::isNull)) { |
| throw new IllegalArgumentException("Collection with sub controllers contains a null entry!"); |
| } |
| |
| return Collections.unmodifiableCollection(ctrls); |
| } |
| |
| /** The collection with managed reloading controllers. */ |
| private final Collection<ReloadingController> controllers; |
| |
| /** The reloading detector used by this instance. */ |
| private final ReloadingDetector detector; |
| |
| /** |
| * Creates a new instance of {@code CombinedReloadingController} and initializes it with the {@code ReloadingController} |
| * objects to be managed. |
| * |
| * @param subCtrls the collection with sub {@code ReloadingController}s (must not be <strong>null</strong> or contain <strong>null</strong> |
| * entries) |
| * @throws IllegalArgumentException if the passed in collection is <strong>null</strong> or contains <strong>null</strong> entries |
| */ |
| public CombinedReloadingController(final Collection<? extends ReloadingController> subCtrls) { |
| super(DUMMY); |
| controllers = checkManagedControllers(subCtrls); |
| detector = new MultiReloadingControllerDetector(this); |
| } |
| |
| /** |
| * {@inheritDoc} This implementation returns a special reloading detector which operates on all managed controllers. |
| */ |
| @Override |
| public ReloadingDetector getDetector() { |
| return detector; |
| } |
| |
| /** |
| * Gets a (unmodifiable) collection with the sub controllers managed by this combined controller. |
| * |
| * @return a collection with sub controllers |
| */ |
| public Collection<ReloadingController> getSubControllers() { |
| return controllers; |
| } |
| |
| /** |
| * Resets the reloading state of all managed sub controllers unconditionally. This method is intended to be called after |
| * the creation of an instance. It may be the case that some of the sub controllers are already in reloading state, so |
| * their state is out of sync with this controller's global reloading state. This method ensures that the reloading |
| * state of all sub controllers is reset. |
| */ |
| public void resetInitialReloadingState() { |
| getDetector().reloadingPerformed(); |
| } |
| } |