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