org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder Maven / Gradle / Ivy
Show all versions of commons-configuration2 Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.configuration2.builder;
import java.util.Map;
import org.apache.commons.configuration2.FileBasedConfiguration;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.io.FileHandler;
import org.apache.commons.configuration2.reloading.ReloadingController;
import org.apache.commons.configuration2.reloading.ReloadingControllerSupport;
import org.apache.commons.configuration2.reloading.ReloadingDetector;
/**
*
* A specialized {@code ConfigurationBuilder} implementation which can handle configurations read from a
* {@link FileHandler} and supports reloading.
*
*
* This builder class exposes a {@link ReloadingController} object controlling reload operations on the file-based
* configuration produced as result object. For the {@code FileHandler} defining the location of the configuration a
* configurable {@link ReloadingDetector} is created and associated with the controller. So changes on the source file
* can be detected. When ever such a change occurs, the result object of this builder is reset. This means that the next
* time {@code getConfiguration()} is called a new {@code Configuration} object is created which is loaded from the
* modified file.
*
*
* Client code interested in notifications can register a listener at this builder to receive reset events. When such an
* event is received the new result object can be requested. This way client applications can be sure to work with an
* up-to-date configuration. It is also possible to register a listener directly at the {@code ReloadingController}.
*
*
* This builder does not actively trigger the {@code ReloadingController} to perform a reload check. This has to be done
* by an external component, e.g. a timer.
*
*
* @since 2.0
* @param the concrete type of {@code Configuration} objects created by this builder
*/
public class ReloadingFileBasedConfigurationBuilder extends FileBasedConfigurationBuilder
implements ReloadingControllerSupport {
/** The default factory for creating reloading detector objects. */
private static final ReloadingDetectorFactory DEFAULT_DETECTOR_FACTORY = new DefaultReloadingDetectorFactory();
/**
* Returns a {@code ReloadingDetectorFactory} either from the passed in parameters or a default factory.
*
* @param params the current parameters object
* @return the {@code ReloadingDetectorFactory} to be used
*/
private static ReloadingDetectorFactory fetchDetectorFactory(final FileBasedBuilderParametersImpl params) {
final ReloadingDetectorFactory factory = params.getReloadingDetectorFactory();
return factory != null ? factory : DEFAULT_DETECTOR_FACTORY;
}
/** The reloading controller associated with this object. */
private final ReloadingController reloadingController;
/**
* The reloading detector which does the actual reload check for the current result object. A new instance is created
* whenever a new result object (and thus a new current file handler) becomes available. The field must be volatile
* because it is accessed by the reloading controller probably from within another thread.
*/
private volatile ReloadingDetector resultReloadingDetector;
/**
* Creates a new instance of {@code ReloadingFileBasedConfigurationBuilder} which produces result objects of the
* specified class.
*
* @param resCls the result class (must not be null
* @throws IllegalArgumentException if the result class is null
*/
public ReloadingFileBasedConfigurationBuilder(final Class extends T> resCls) {
super(resCls);
reloadingController = createReloadingController();
}
/**
* Creates a new instance of {@code ReloadingFileBasedConfigurationBuilder} which produces result objects of the
* specified class and sets initialization parameters.
*
* @param resCls the result class (must not be null
* @param params a map with initialization parameters
* @throws IllegalArgumentException if the result class is null
*/
public ReloadingFileBasedConfigurationBuilder(final Class extends T> resCls, final Map params) {
super(resCls, params);
reloadingController = createReloadingController();
}
/**
* Creates a new instance of {@code ReloadingFileBasedConfigurationBuilder} which produces result objects of the
* specified class and sets initialization parameters and the allowFailOnInit flag.
*
* @param resCls the result class (must not be null
* @param params a map with initialization parameters
* @param allowFailOnInit the allowFailOnInit flag
* @throws IllegalArgumentException if the result class is null
*/
public ReloadingFileBasedConfigurationBuilder(final Class extends T> resCls, final Map params, final boolean allowFailOnInit) {
super(resCls, params, allowFailOnInit);
reloadingController = createReloadingController();
}
/**
* {@inheritDoc} This method is overridden here to change the result type.
*/
@Override
public ReloadingFileBasedConfigurationBuilder configure(final BuilderParameters... params) {
super.configure(params);
return this;
}
/**
* Creates the {@code ReloadingController} associated with this object. The controller is assigned a specialized
* reloading detector which delegates to the detector for the current result object. (
* {@code FileHandlerReloadingDetector} does not support changing the file handler, and {@code ReloadingController} does
* not support changing the reloading detector; therefore, this level of indirection is needed to change the monitored
* file dynamically.)
*
* @return the new {@code ReloadingController}
*/
private ReloadingController createReloadingController() {
final ReloadingDetector ctrlDetector = createReloadingDetectorForController();
final ReloadingController ctrl = new ReloadingController(ctrlDetector);
connectToReloadingController(ctrl);
return ctrl;
}
/**
* Creates a {@code ReloadingDetector} which monitors the passed in {@code FileHandler}. This method is called each time
* a new result object is created with the current {@code FileHandler}. This implementation checks whether a
* {@code ReloadingDetectorFactory} is specified in the current parameters. If this is the case, it is invoked.
* Otherwise, a default factory is used to create a {@code FileHandlerReloadingDetector} object. Note: This method is
* called from a synchronized block.
*
* @param handler the current {@code FileHandler}
* @param fbparams the object with parameters related to file-based builders
* @return a {@code ReloadingDetector} for this {@code FileHandler}
* @throws ConfigurationException if an error occurs
*/
protected ReloadingDetector createReloadingDetector(final FileHandler handler, final FileBasedBuilderParametersImpl fbparams)
throws ConfigurationException {
return fetchDetectorFactory(fbparams).createReloadingDetector(handler, fbparams);
}
/**
* Creates a {@code ReloadingDetector} wrapper to be passed to the associated {@code ReloadingController}. This detector
* wrapper simply delegates to the current {@code ReloadingDetector} if it is available.
*
* @return the wrapper {@code ReloadingDetector}
*/
private ReloadingDetector createReloadingDetectorForController() {
return new ReloadingDetector() {
@Override
public boolean isReloadingRequired() {
final ReloadingDetector detector = resultReloadingDetector;
return detector != null && detector.isReloadingRequired();
}
@Override
public void reloadingPerformed() {
final ReloadingDetector detector = resultReloadingDetector;
if (detector != null) {
detector.reloadingPerformed();
}
}
};
}
/**
* Gets the {@code ReloadingController} associated with this builder. This controller is directly created. However,
* it becomes active (i.e. associated with a meaningful reloading detector) not before a result object was created.
*
* @return the {@code ReloadingController}
*/
@Override
public ReloadingController getReloadingController() {
return reloadingController;
}
/**
* {@inheritDoc} This implementation also takes care that a new {@code ReloadingDetector} for the new current
* {@code FileHandler} is created. Also, the reloading controller's reloading state has to be reset; after the creation
* of a new result object changes in the underlying configuration source have to be monitored again.
*/
@Override
protected void initFileHandler(final FileHandler handler) throws ConfigurationException {
super.initFileHandler(handler);
resultReloadingDetector = createReloadingDetector(handler, FileBasedBuilderParametersImpl.fromParameters(getParameters(), true));
}
}