    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}