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 }