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.builder.combined;
18  
19  import java.util.Collection;
20  import java.util.LinkedList;
21  import java.util.Map;
22  
23  import org.apache.commons.configuration2.CombinedConfiguration;
24  import org.apache.commons.configuration2.HierarchicalConfiguration;
25  import org.apache.commons.configuration2.XMLConfiguration;
26  import org.apache.commons.configuration2.builder.BuilderParameters;
27  import org.apache.commons.configuration2.builder.ConfigurationBuilder;
28  import org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder;
29  import org.apache.commons.configuration2.ex.ConfigurationException;
30  import org.apache.commons.configuration2.reloading.CombinedReloadingController;
31  import org.apache.commons.configuration2.reloading.ReloadingController;
32  import org.apache.commons.configuration2.reloading.ReloadingControllerSupport;
33  
34  /**
35   * <p>
36   * An extension of {@code CombinedConfigurationBuilder} which also supports reloading operations.
37   * </p>
38   * <p>
39   * This class differs from its super class in the following aspects:
40   * </p>
41   * <ul>
42   * <li>A {@link ReloadingController} is created which manages all child configuration builders supporting reloading
43   * operations.</li>
44   * <li>If no {@code ConfigurationBuilder} is provided for the definition configuration, a builder with reloading support
45   * is created.</li>
46   * </ul>
47   * <p>
48   * This class can be used exactly as its super class for creating combined configurations from multiple configuration
49   * sources. In addition, the combined reloading controller managed by an instance can be used to react on changes in one
50   * of these configuration sources or in the definition configuration.
51   * </p>
52   *
53   * @since 2.0
54   */
55  public class ReloadingCombinedConfigurationBuilder extends CombinedConfigurationBuilder implements ReloadingControllerSupport {
56  
57      /**
58       * Checks whether the passed in builder object supports reloading. If yes, its reloading controller is obtained and
59       * added to the given list.
60       *
61       * @param subControllers the list with sub controllers
62       * @param builder the builder object to be checked
63       */
64      public static void obtainReloadingController(final Collection<ReloadingController> subControllers, final Object builder) {
65          if (builder instanceof ReloadingControllerSupport) {
66              subControllers.add(((ReloadingControllerSupport) builder).getReloadingController());
67          }
68      }
69  
70      /** The reloading controller used by this builder. */
71      private ReloadingController reloadingController;
72  
73      /**
74       * Creates a new instance of {@code ReloadingCombinedConfigurationBuilder}. No parameters are set.
75       */
76      public ReloadingCombinedConfigurationBuilder() {
77      }
78  
79      /**
80       * Creates a new instance of {@code ReloadingCombinedConfigurationBuilder} and sets the specified initialization
81       * parameters.
82       *
83       * @param params a map with initialization parameters
84       */
85      public ReloadingCombinedConfigurationBuilder(final Map<String, Object> params) {
86          super(params);
87      }
88  
89      /**
90       * Creates a new instance of {@code ReloadingCombinedConfigurationBuilder} and sets the specified initialization
91       * parameters and the <em>allowFailOnInit</em> flag.
92       *
93       * @param params a map with initialization parameters
94       * @param allowFailOnInit the <em>allowFailOnInit</em> flag
95       */
96      public ReloadingCombinedConfigurationBuilder(final Map<String, Object> params, final boolean allowFailOnInit) {
97          super(params, allowFailOnInit);
98      }
99  
100     /**
101      * {@inheritDoc} This method is overridden to adapt the return type.
102      */
103     @Override
104     public ReloadingCombinedConfigurationBuilder configure(final BuilderParameters... params) {
105         super.configure(params);
106         return this;
107     }
108 
109     /**
110      * Creates the {@code ReloadingController} for this builder. This method is called after the result configuration has
111      * been created and initialized. It is called from a synchronized block. This implementation creates a
112      * {@link CombinedReloadingController}.
113      *
114      * @return the {@code ReloadingController} for this builder
115      * @throws ConfigurationException if an error occurs
116      */
117     protected ReloadingController createReloadingController() throws ConfigurationException {
118         final Collection<ReloadingController> subControllers = new LinkedList<>();
119         final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder = getDefinitionBuilder();
120         obtainReloadingController(subControllers, defBuilder);
121 
122         getChildBuilders().forEach(b -> obtainReloadingController(subControllers, b));
123 
124         final CombinedReloadingController ctrl = new CombinedReloadingController(subControllers);
125         ctrl.resetInitialReloadingState();
126         return ctrl;
127     }
128 
129     /**
130      * {@inheritDoc} This implementation creates a builder for XML configurations with reloading support.
131      */
132     @Override
133     protected ConfigurationBuilder<? extends HierarchicalConfiguration<?>> createXMLDefinitionBuilder(final BuilderParameters builderParams) {
134         return new ReloadingFileBasedConfigurationBuilder<>(XMLConfiguration.class).configure(builderParams);
135     }
136 
137     /**
138      * {@inheritDoc} This implementation makes sure that the reloading state of the managed reloading controller is reset.
139      * Note that this has to be done here and not in {@link #initResultInstance(CombinedConfiguration)} because it must be
140      * outside of a synchronized block; otherwise, a dead-lock situation can occur.
141      */
142     @Override
143     public CombinedConfiguration getConfiguration() throws ConfigurationException {
144         final CombinedConfiguration result = super.getConfiguration();
145         reloadingController.resetReloadingState();
146         return result;
147     }
148 
149     /**
150      * {@inheritDoc} This implementation returns a {@link CombinedReloadingController} which contains sub controllers for
151      * all child configuration sources with reloading support. If the definition builder supports reloading, its controller
152      * is contained, too. Note that the combined reloading controller is initialized when the result configuration is
153      * created (i.e. when calling {@code getConfiguration()} for the first time). So this method does not return a
154      * meaningful result before.
155      */
156     @Override
157     public synchronized ReloadingController getReloadingController() {
158         return reloadingController;
159     }
160 
161     /**
162      * {@inheritDoc} This implementation first calls the super method to actually initialize the result configuration. Then
163      * it creates the {@link CombinedReloadingController} for all child configuration sources with reloading support.
164      */
165     @Override
166     protected void initResultInstance(final CombinedConfiguration result) throws ConfigurationException {
167         super.initResultInstance(result);
168         if (reloadingController == null) {
169             reloadingController = createReloadingController();
170         }
171     }
172 }