    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 * @since 2.0
    057 */
    058public class ReloadingCombinedConfigurationBuilder extends
    059        CombinedConfigurationBuilder implements ReloadingControllerSupport
    060{
    061    /** The reloading controller used by this builder. */
    062    private ReloadingController reloadingController;
    063
    064    /**
    065     * Creates a new instance of {@code ReloadingCombinedConfigurationBuilder}.
    066     * No parameters are set.
    067     */
    068    public ReloadingCombinedConfigurationBuilder()
    069    {
    070        super();
    071    }
    072
    073    /**
    074     * Creates a new instance of {@code ReloadingCombinedConfigurationBuilder}
    075     * and sets the specified initialization parameters and the
    076     * <em>allowFailOnInit</em> flag.
    077     *
    078     * @param params a map with initialization parameters
    079     * @param allowFailOnInit the <em>allowFailOnInit</em> flag
    080     */
    081    public ReloadingCombinedConfigurationBuilder(final Map<String, Object> params,
    082            final boolean allowFailOnInit)
    083    {
    084        super(params, allowFailOnInit);
    085    }
    086
    087    /**
    088     * Creates a new instance of {@code ReloadingCombinedConfigurationBuilder}
    089     * and sets the specified initialization parameters.
    090     *
    091     * @param params a map with initialization parameters
    092     */
    093    public ReloadingCombinedConfigurationBuilder(final Map<String, Object> params)
    094    {
    095        super(params);
    096    }
    097
    098    /**
    099     * {@inheritDoc} This method is overridden to adapt the return type.
    100     */
    101    @Override
    102    public ReloadingCombinedConfigurationBuilder configure(final BuilderParameters... params)
    103    {
    104        super.configure(params);
    105        return this;
    106    }
    107
    108    /**
    109     * {@inheritDoc} This implementation returns a
    110     * {@link CombinedReloadingController} which contains sub controllers for
    111     * all child configuration sources with reloading support. If the definition
    112     * builder supports reloading, its controller is contained, too. Note that
    113     * the combined reloading controller is initialized when the result
    114     * configuration is created (i.e. when calling {@code getConfiguration()}
    115     * for the first time). So this method does not return a meaningful result
    116     * before.
    117     */
    118    @Override
    119    public synchronized ReloadingController getReloadingController()
    120    {
    121        return reloadingController;
    122    }
    123
    124    /**
    125     * {@inheritDoc} This implementation makes sure that the reloading state of
    126     * the managed reloading controller is reset. Note that this has to be done
    127     * here and not in {@link #initResultInstance(CombinedConfiguration)}
    128     * because it must be outside of a synchronized block; otherwise, a
    129     * dead-lock situation can occur.
    130     */
    131    @Override
    132    public CombinedConfiguration getConfiguration() throws ConfigurationException
    133    {
    134        final CombinedConfiguration result = super.getConfiguration();
    135        reloadingController.resetReloadingState();
    136        return result;
    137    }
    138
    139    /**
    140     * {@inheritDoc} This implementation creates a builder for XML
    141     * configurations with reloading support.
    142     */
    143    @Override
    144    protected ConfigurationBuilder<? extends HierarchicalConfiguration<?>> createXMLDefinitionBuilder(
    145            final BuilderParameters builderParams)
    146    {
    147        return new ReloadingFileBasedConfigurationBuilder<>(
    148                XMLConfiguration.class).configure(builderParams);
    149    }
    150
    151    /**
    152     * {@inheritDoc} This implementation first calls the super method to
    153     * actually initialize the result configuration. Then it creates the
    154     * {@link CombinedReloadingController} for all child configuration sources
    155     * with reloading support.
    156     */
    157    @Override
    158    protected void initResultInstance(final CombinedConfiguration result)
    159            throws ConfigurationException
    160    {
    161        super.initResultInstance(result);
    162        if (reloadingController == null)
    163        {
    164            reloadingController = createReloadingController();
    165        }
    166    }
    167
    168    /**
    169     * Creates the {@code ReloadingController} for this builder. This method is
    170     * called after the result configuration has been created and initialized.
    171     * It is called from a synchronized block. This implementation creates a
    172     * {@link CombinedReloadingController}.
    173     *
    174     * @return the {@code ReloadingController} for this builder
    175     * @throws ConfigurationException if an error occurs
    176     */
    177    protected ReloadingController createReloadingController()
    178            throws ConfigurationException
    179    {
    180        final Collection<ReloadingController> subControllers =
    181                new LinkedList<>();
    182        final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder =
    183                getDefinitionBuilder();
    184        obtainReloadingController(subControllers, defBuilder);
    185
    186        for (final ConfigurationBuilder<? extends Configuration> b : getChildBuilders())
    187        {
    188            obtainReloadingController(subControllers, b);
    189        }
    190
    191        final CombinedReloadingController ctrl =
    192                new CombinedReloadingController(subControllers);
    193        ctrl.resetInitialReloadingState();
    194        return ctrl;
    195    }
    196
    197    /**
    198     * Checks whether the passed in builder object supports reloading. If yes,
    199     * its reloading controller is obtained and added to the given list.
    200     *
    201     * @param subControllers the list with sub controllers
    202     * @param builder the builder object to be checked
    203     */
    204    public static void obtainReloadingController(
    205            final Collection<ReloadingController> subControllers, final Object builder)
    206    {
    207        if (builder instanceof ReloadingControllerSupport)
    208        {
    209            subControllers.add(((ReloadingControllerSupport) builder)
    210                    .getReloadingController());
    211        }
    212    }
    213}