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.builder.combined;
018
019import java.util.Collection;
020import java.util.LinkedList;
021import java.util.Map;
022
023import org.apache.commons.configuration2.CombinedConfiguration;
024import org.apache.commons.configuration2.Configuration;
025import org.apache.commons.configuration2.HierarchicalConfiguration;
026import org.apache.commons.configuration2.XMLConfiguration;
027import org.apache.commons.configuration2.builder.BuilderParameters;
028import org.apache.commons.configuration2.builder.ConfigurationBuilder;
029import org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder;
030import org.apache.commons.configuration2.ex.ConfigurationException;
031import org.apache.commons.configuration2.reloading.CombinedReloadingController;
032import org.apache.commons.configuration2.reloading.ReloadingController;
033import org.apache.commons.configuration2.reloading.ReloadingControllerSupport;
034
035/**
036 * <p>
037 * An extension of {@code CombinedConfigurationBuilder} which also supports
038 * reloading operations.
039 * </p>
040 * <p>
041 * This class differs from its super class in the following aspects:
042 * </p>
043 * <ul>
044 * <li>A {@link ReloadingController} is created which manages all child
045 * configuration builders supporting reloading operations.</li>
046 * <li>If no {@code ConfigurationBuilder} is provided for the definition
047 * configuration, a builder with reloading support is created.</li>
048 * </ul>
049 * <p>
050 * This class can be used exactly as its super class for creating combined
051 * configurations from multiple configuration sources. In addition, the combined
052 * reloading controller managed by an instance can be used to react on changes
053 * in one of these configuration sources or in the definition configuration.
054 * </p>
055 *
056 * @version $Id: ReloadingCombinedConfigurationBuilder.java 1831358 2018-05-10 17:26:21Z oheger $
057 * @since 2.0
058 */
059public class ReloadingCombinedConfigurationBuilder extends
060        CombinedConfigurationBuilder implements ReloadingControllerSupport
061{
062    /** The reloading controller used by this builder. */
063    private ReloadingController reloadingController;
064
065    /**
066     * Creates a new instance of {@code ReloadingCombinedConfigurationBuilder}.
067     * No parameters are set.
068     */
069    public ReloadingCombinedConfigurationBuilder()
070    {
071        super();
072    }
073
074    /**
075     * Creates a new instance of {@code ReloadingCombinedConfigurationBuilder}
076     * and sets the specified initialization parameters and the
077     * <em>allowFailOnInit</em> flag.
078     *
079     * @param params a map with initialization parameters
080     * @param allowFailOnInit the <em>allowFailOnInit</em> flag
081     */
082    public ReloadingCombinedConfigurationBuilder(Map<String, Object> params,
083            boolean allowFailOnInit)
084    {
085        super(params, allowFailOnInit);
086    }
087
088    /**
089     * Creates a new instance of {@code ReloadingCombinedConfigurationBuilder}
090     * and sets the specified initialization parameters.
091     *
092     * @param params a map with initialization parameters
093     */
094    public ReloadingCombinedConfigurationBuilder(Map<String, Object> params)
095    {
096        super(params);
097    }
098
099    /**
100     * {@inheritDoc} This method is overridden to adapt the return type.
101     */
102    @Override
103    public ReloadingCombinedConfigurationBuilder configure(BuilderParameters... params)
104    {
105        super.configure(params);
106        return this;
107    }
108
109    /**
110     * {@inheritDoc} This implementation returns a
111     * {@link CombinedReloadingController} which contains sub controllers for
112     * all child configuration sources with reloading support. If the definition
113     * builder supports reloading, its controller is contained, too. Note that
114     * the combined reloading controller is initialized when the result
115     * configuration is created (i.e. when calling {@code getConfiguration()}
116     * for the first time). So this method does not return a meaningful result
117     * before.
118     */
119    @Override
120    public synchronized ReloadingController getReloadingController()
121    {
122        return reloadingController;
123    }
124
125    /**
126     * {@inheritDoc} This implementation makes sure that the reloading state of
127     * the managed reloading controller is reset. Note that this has to be done
128     * here and not in {@link #initResultInstance(CombinedConfiguration)}
129     * because it must be outside of a synchronized block; otherwise, a
130     * dead-lock situation can occur.
131     */
132    @Override
133    public CombinedConfiguration getConfiguration() throws ConfigurationException
134    {
135        CombinedConfiguration result = super.getConfiguration();
136        reloadingController.resetReloadingState();
137        return result;
138    }
139
140    /**
141     * {@inheritDoc} This implementation creates a builder for XML
142     * configurations with reloading support.
143     */
144    @Override
145    protected ConfigurationBuilder<? extends HierarchicalConfiguration<?>> createXMLDefinitionBuilder(
146            BuilderParameters builderParams)
147    {
148        return new ReloadingFileBasedConfigurationBuilder<>(
149                XMLConfiguration.class).configure(builderParams);
150    }
151
152    /**
153     * {@inheritDoc} This implementation first calls the super method to
154     * actually initialize the result configuration. Then it creates the
155     * {@link CombinedReloadingController} for all child configuration sources
156     * with reloading support.
157     */
158    @Override
159    protected void initResultInstance(CombinedConfiguration result)
160            throws ConfigurationException
161    {
162        super.initResultInstance(result);
163        if (reloadingController == null)
164        {
165            reloadingController = createReloadingController();
166        }
167    }
168
169    /**
170     * Creates the {@code ReloadingController} for this builder. This method is
171     * called after the result configuration has been created and initialized.
172     * It is called from a synchronized block. This implementation creates a
173     * {@link CombinedReloadingController}.
174     *
175     * @return the {@code ReloadingController} for this builder
176     * @throws ConfigurationException if an error occurs
177     */
178    protected ReloadingController createReloadingController()
179            throws ConfigurationException
180    {
181        Collection<ReloadingController> subControllers =
182                new LinkedList<>();
183        ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder =
184                getDefinitionBuilder();
185        obtainReloadingController(subControllers, defBuilder);
186
187        for (ConfigurationBuilder<? extends Configuration> b : getChildBuilders())
188        {
189            obtainReloadingController(subControllers, b);
190        }
191
192        CombinedReloadingController ctrl =
193                new CombinedReloadingController(subControllers);
194        ctrl.resetInitialReloadingState();
195        return ctrl;
196    }
197
198    /**
199     * Checks whether the passed in builder object supports reloading. If yes,
200     * its reloading controller is obtained and added to the given list.
201     *
202     * @param subControllers the list with sub controllers
203     * @param builder the builder object to be checked
204     */
205    public static void obtainReloadingController(
206            Collection<ReloadingController> subControllers, Object builder)
207    {
208        if (builder instanceof ReloadingControllerSupport)
209        {
210            subControllers.add(((ReloadingControllerSupport) builder)
211                    .getReloadingController());
212        }
213    }
214}