View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration2.reloading;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.Objects;
23  
24  /**
25   * <p>
26   * A specialized {@code ReloadingController} implementation which manages an arbitrary number of other
27   * {@code ReloadingController} objects.
28   * </p>
29   * <p>
30   * This class can be used to handle multiple simple controllers for reload operations as a single object. As a usage
31   * example consider a combined configuration containing a number of configuration sources of which some support
32   * reloading. In this scenario all {@code ReloadingController} instances for the reloading-enabled sources can be added
33   * to a {@code CombinedReloadingController}. Then by triggering the combined controller a reload check is performed on
34   * all child sources.
35   * </p>
36   * <p>
37   * This class is a typical implementation of the <em>composite pattern</em>. An instance is constructed with a
38   * collection of sub {@code ReloadingController} objects. Its operations are implemented by delegating to all child
39   * controllers.
40   * </p>
41   * <p>
42   * This class expects the managed controller objects to be passed to the constructor. From this list a defensive copy is
43   * created so that it cannot be changed later on. Derived classes can override the {@link #getSubControllers()} method
44   * if they need another way to handle child controllers (for example a more dynamic way). However, they are then responsible to
45   * ensure a safe access to this list in a multi-threaded environment.
46   * </p>
47   *
48   * @since 2.0
49   */
50  public class CombinedReloadingController extends ReloadingController {
51  
52      /**
53       * A specialized implementation of the {@code ReloadingDetector} interface which operates on a collection of
54       * {@code ReloadingController} objects. The methods defined by the {@code ReloadingDetector} interface are delegated to
55       * the managed controllers.
56       */
57      private static final class MultiReloadingControllerDetector implements ReloadingDetector {
58  
59          /** A reference to the owning combined reloading controller. */
60          private final CombinedReloadingController owner;
61  
62          /**
63           * Creates a new instance of {@code MultiReloadingControllerDetector}.
64           *
65           * @param owner the owner
66           */
67          public MultiReloadingControllerDetector(final CombinedReloadingController owner) {
68              this.owner = owner;
69          }
70  
71          /**
72           * {@inheritDoc} This implementation delegates to the managed controllers. For all of them the
73           * {@code checkForReloading()} method is called, giving them the chance to trigger a reload if necessary. If one of
74           * these calls returns <strong>true</strong>, the result of this method is <strong>true</strong>, otherwise <strong>false</strong>.
75           */
76          @Override
77          public boolean isReloadingRequired() {
78              return owner.getSubControllers().stream().reduce(false, (b, rc) -> b | rc.checkForReloading(null), (t, u) -> t | u);
79          }
80  
81          /**
82           * {@inheritDoc} This implementation resets the reloading state on all managed controllers.
83           */
84          @Override
85          public void reloadingPerformed() {
86              owner.getSubControllers().forEach(ReloadingController::resetReloadingState);
87          }
88      }
89  
90      /** Constant for a dummy reloading detector. */
91      private static final ReloadingDetector DUMMY = new MultiReloadingControllerDetector(null);
92  
93      /**
94       * Checks the collection with the passed in sub controllers and creates a defensive copy.
95       *
96       * @param subCtrls the collection with sub controllers
97       * @return a copy of the collection to be stored in the newly created instance
98       * @throws IllegalArgumentException if the passed in collection is <strong>null</strong> or contains <strong>null</strong> entries
99       */
100     private static Collection<ReloadingController> checkManagedControllers(final Collection<? extends ReloadingController> subCtrls) {
101         if (subCtrls == null) {
102             throw new IllegalArgumentException("Collection with sub controllers must not be null.");
103         }
104         final Collection<ReloadingController> ctrls = new ArrayList<>(subCtrls);
105         if (ctrls.stream().anyMatch(Objects::isNull)) {
106             throw new IllegalArgumentException("Collection with sub controllers contains a null entry.");
107         }
108 
109         return Collections.unmodifiableCollection(ctrls);
110     }
111 
112     /** The collection with managed reloading controllers. */
113     private final Collection<ReloadingController> controllers;
114 
115     /** The reloading detector used by this instance. */
116     private final ReloadingDetector detector;
117 
118     /**
119      * Creates a new instance of {@code CombinedReloadingController} and initializes it with the {@code ReloadingController}
120      * objects to be managed.
121      *
122      * @param subCtrls the collection with sub {@code ReloadingController}s (must not be <strong>null</strong> or contain <strong>null</strong>
123      *        entries)
124      * @throws IllegalArgumentException if the passed in collection is <strong>null</strong> or contains <strong>null</strong> entries
125      */
126     public CombinedReloadingController(final Collection<? extends ReloadingController> subCtrls) {
127         super(DUMMY);
128         controllers = checkManagedControllers(subCtrls);
129         detector = new MultiReloadingControllerDetector(this);
130     }
131 
132     /**
133      * {@inheritDoc} This implementation returns a special reloading detector which operates on all managed controllers.
134      */
135     @Override
136     public ReloadingDetector getDetector() {
137         return detector;
138     }
139 
140     /**
141      * Gets a (unmodifiable) collection with the sub controllers managed by this combined controller.
142      *
143      * @return a collection with sub controllers
144      */
145     public Collection<ReloadingController> getSubControllers() {
146         return controllers;
147     }
148 
149     /**
150      * Resets the reloading state of all managed sub controllers unconditionally. This method is intended to be called after
151      * the creation of an instance. It may be the case that some of the sub controllers are already in reloading state, so
152      * their state is out of sync with this controller's global reloading state. This method ensures that the reloading
153      * state of all sub controllers is reset.
154      */
155     public void resetInitialReloadingState() {
156         getDetector().reloadingPerformed();
157     }
158 }