    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 1842194 2018-09-27 22:24:23Z ggregory $
    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(final Map<String, Object> params,
    083            final 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(final 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(final 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        final 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            final 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(final 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        final Collection<ReloadingController> subControllers =
    182                new LinkedList<>();
    183        final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder =
    184                getDefinitionBuilder();
    185        obtainReloadingController(subControllers, defBuilder);
    186
    187        for (final ConfigurationBuilder<? extends Configuration> b : getChildBuilders())
    188        {
    189            obtainReloadingController(subControllers, b);
    190        }
    191
    192        final 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            final Collection<ReloadingController> subControllers, final Object builder)
    207    {
    208        if (builder instanceof ReloadingControllerSupport)
    209        {
    210            subControllers.add(((ReloadingControllerSupport) builder)
    211                    .getReloadingController());
    212        }
    213    }
    214}