BaseConfigurationBuilderProvider.java
- /*
- * 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.combined;
- import java.lang.reflect.Constructor;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.Map;
- import org.apache.commons.configuration2.Configuration;
- import org.apache.commons.configuration2.ConfigurationUtils;
- import org.apache.commons.configuration2.builder.BasicConfigurationBuilder;
- import org.apache.commons.configuration2.builder.BuilderParameters;
- import org.apache.commons.configuration2.builder.ConfigurationBuilder;
- import org.apache.commons.configuration2.ex.ConfigurationException;
- /**
- * <p>
- * A fully-functional, reflection-based implementation of the {@code ConfigurationBuilderProvider} interface which can
- * deal with the default tags defining configuration sources.
- * </p>
- * <p>
- * An instance of this class is initialized with the names of the {@code ConfigurationBuilder} class used by this
- * provider and the concrete {@code Configuration} class. The {@code ConfigurationBuilder} class must be derived from
- * {@link BasicConfigurationBuilder}. When asked for the builder object, an instance of the builder class is created and
- * initialized from the bean declaration associated with the current configuration source.
- * </p>
- * <p>
- * {@code ConfigurationBuilder} objects are configured using parameter objects. When declaring configuration sources in
- * XML it should not be necessary to define the single parameter objects. Rather, simple and complex properties are set
- * in the typical way of a bean declaration (i.e. as attributes of the current XML element or as child elements). This
- * class creates all supported parameter objects (whose names also must be provided at construction time) and takes care
- * that their properties are initialized according to the current bean declaration.
- * </p>
- * <p>
- * The use of reflection to create builder instances allows a generic implementation supporting many concrete builder
- * classes. Another reason for this approach is that builder classes are only loaded if actually needed. Some
- * specialized {@code Configuration} implementations require specific external dependencies which should not be
- * mandatory for the use of {@code CombinedConfigurationBuilder}. Because such classes are lazily loaded, an application
- * only has to include the dependencies it actually uses.
- * </p>
- *
- * @since 2.0
- */
- public class BaseConfigurationBuilderProvider implements ConfigurationBuilderProvider {
- /** The types of the constructor parameters for a basic builder. */
- private static final Class<?>[] CTOR_PARAM_TYPES = {Class.class, Map.class, Boolean.TYPE};
- /**
- * Creates an instance of a parameter class using reflection.
- *
- * @param paramcls the parameter class
- * @return the newly created instance
- * @throws Exception if an error occurs
- */
- private static BuilderParameters createParameterObject(final String paramcls) throws ReflectiveOperationException {
- return (BuilderParameters) ConfigurationUtils.loadClass(paramcls).getConstructor().newInstance();
- }
- /**
- * Creates a new, unmodifiable collection for the parameter classes.
- *
- * @param paramCls the collection with parameter classes passed to the constructor
- * @return the collection to be stored
- */
- private static Collection<String> initParameterClasses(final Collection<String> paramCls) {
- if (paramCls == null) {
- return Collections.emptySet();
- }
- return Collections.unmodifiableCollection(new ArrayList<>(paramCls));
- }
- /** The name of the builder class. */
- private final String builderClass;
- /** The name of a builder class with reloading support. */
- private final String reloadingBuilderClass;
- /** Stores the name of the configuration class to be created. */
- private final String configurationClass;
- /** A collection with the names of parameter classes. */
- private final Collection<String> parameterClasses;
- /**
- * Creates a new instance of {@code BaseConfigurationBuilderProvider} and initializes all its properties.
- *
- * @param bldrCls the name of the builder class (must not be <strong>null</strong>)
- * @param reloadBldrCls the name of a builder class to be used if reloading support is required (<strong>null</strong> if
- * reloading is not supported)
- * @param configCls the name of the configuration class (must not be <strong>null</strong>)
- * @param paramCls a collection with the names of parameters classes
- * @throws IllegalArgumentException if a required parameter is missing
- */
- public BaseConfigurationBuilderProvider(final String bldrCls, final String reloadBldrCls, final String configCls, final Collection<String> paramCls) {
- if (bldrCls == null) {
- throw new IllegalArgumentException("Builder class must not be null!");
- }
- if (configCls == null) {
- throw new IllegalArgumentException("Configuration class must not be null!");
- }
- builderClass = bldrCls;
- reloadingBuilderClass = reloadBldrCls;
- configurationClass = configCls;
- parameterClasses = initParameterClasses(paramCls);
- }
- /**
- * Configures a newly created builder instance with its initialization parameters. This method is called after a new
- * instance was created using reflection. This implementation passes the parameter objects to the builder's
- * {@code configure()} method.
- *
- * @param builder the builder to be initialized
- * @param decl the current {@code ConfigurationDeclaration}
- * @param params the collection with initialization parameter objects
- * @throws Exception if an error occurs
- */
- protected void configureBuilder(final BasicConfigurationBuilder<? extends Configuration> builder, final ConfigurationDeclaration decl,
- final Collection<BuilderParameters> params) throws Exception {
- builder.configure(params.toArray(new BuilderParameters[params.size()]));
- }
- /**
- * Creates a new, uninitialized instance of the builder class managed by this provider. This implementation determines
- * the builder class to be used by delegating to {@code determineBuilderClass()}. It then calls the constructor
- * expecting the configuration class, the map with properties, and the<em>allowFailOnInit</em> flag.
- *
- * @param decl the current {@code ConfigurationDeclaration}
- * @param params initialization parameters for the new builder object
- * @return the newly created builder instance
- * @throws Exception if an error occurs
- */
- protected BasicConfigurationBuilder<? extends Configuration> createBuilder(final ConfigurationDeclaration decl, final Collection<BuilderParameters> params)
- throws Exception {
- final Class<?> bldCls = ConfigurationUtils.loadClass(determineBuilderClass(decl));
- final Class<?> configCls = ConfigurationUtils.loadClass(determineConfigurationClass(decl, params));
- final Constructor<?> ctor = bldCls.getConstructor(CTOR_PARAM_TYPES);
- // ? extends Configuration is the minimum constraint
- @SuppressWarnings("unchecked")
- final BasicConfigurationBuilder<? extends Configuration> builder = (BasicConfigurationBuilder<? extends Configuration>) ctor.newInstance(configCls,
- null, isAllowFailOnInit(decl));
- return builder;
- }
- /**
- * Creates a collection of parameter objects to be used for configuring the builder. This method creates instances of
- * the parameter classes passed to the constructor.
- *
- * @return a collection with parameter objects for the builder
- * @throws Exception if an error occurs while creating parameter objects via reflection
- */
- protected Collection<BuilderParameters> createParameterObjects() throws Exception {
- final Collection<BuilderParameters> params = new ArrayList<>(getParameterClasses().size());
- for (final String paramcls : getParameterClasses()) {
- params.add(createParameterObject(paramcls));
- }
- return params;
- }
- /**
- * Determines the name of the class to be used for a new builder instance. This implementation selects between the
- * normal and the reloading builder class, based on the passed in {@code ConfigurationDeclaration}. If a reloading
- * builder is desired, but this provider has no reloading support, an exception is thrown.
- *
- * @param decl the current {@code ConfigurationDeclaration}
- * @return the name of the builder class
- * @throws ConfigurationException if the builder class cannot be determined
- */
- protected String determineBuilderClass(final ConfigurationDeclaration decl) throws ConfigurationException {
- if (decl.isReload()) {
- if (getReloadingBuilderClass() == null) {
- throw new ConfigurationException("No support for reloading for builder class " + getBuilderClass());
- }
- return getReloadingBuilderClass();
- }
- return getBuilderClass();
- }
- /**
- * Determines the name of the configuration class produced by the builder. This method is called when obtaining the
- * arguments for invoking the constructor of the builder class. This implementation just returns the pre-configured
- * configuration class name. Derived classes may determine this class name dynamically based on the passed in
- * parameters.
- *
- * @param decl the current {@code ConfigurationDeclaration}
- * @param params the collection with parameter objects
- * @return the name of the builder's result configuration class
- * @throws ConfigurationException if an error occurs
- */
- protected String determineConfigurationClass(final ConfigurationDeclaration decl, final Collection<BuilderParameters> params)
- throws ConfigurationException {
- return getConfigurationClass();
- }
- /**
- * Gets the name of the class of the builder created by this provider.
- *
- * @return the builder class
- */
- public String getBuilderClass() {
- return builderClass;
- }
- /**
- * {@inheritDoc} This implementation delegates to some protected methods to create a new builder instance using
- * reflection and to configure it with parameter values defined by the passed in {@code BeanDeclaration}.
- */
- @Override
- public ConfigurationBuilder<? extends Configuration> getConfigurationBuilder(final ConfigurationDeclaration decl) throws ConfigurationException {
- try {
- final Collection<BuilderParameters> params = createParameterObjects();
- initializeParameterObjects(decl, params);
- final BasicConfigurationBuilder<? extends Configuration> builder = createBuilder(decl, params);
- configureBuilder(builder, decl, params);
- return builder;
- } catch (final ConfigurationException cex) {
- throw cex;
- } catch (final Exception ex) {
- throw new ConfigurationException(ex);
- }
- }
- /**
- * Gets the name of the configuration class created by the builder produced by this provider.
- *
- * @return the configuration class
- */
- public String getConfigurationClass() {
- return configurationClass;
- }
- /**
- * Gets an unmodifiable collection with the names of parameter classes supported by this provider.
- *
- * @return the parameter classes
- */
- public Collection<String> getParameterClasses() {
- return parameterClasses;
- }
- /**
- * Gets the name of the class of the builder created by this provider if the reload flag is set. If this method
- * returns <strong>null</strong>, reloading builders are not supported by this provider.
- *
- * @return the reloading builder class
- */
- public String getReloadingBuilderClass() {
- return reloadingBuilderClass;
- }
- /**
- * Passes all parameter objects to the parent {@code CombinedConfigurationBuilder} so that properties already defined
- * for the parent builder can be added. This method is called before the parameter objects are initialized from the
- * definition configuration. This way properties from the parent builder are inherited, but can be overridden for child
- * configurations.
- *
- * @param decl the current {@code ConfigurationDeclaration}
- * @param params the collection with (uninitialized) parameter objects
- */
- protected void inheritParentBuilderProperties(final ConfigurationDeclaration decl, final Collection<BuilderParameters> params) {
- params.forEach(p -> decl.getConfigurationBuilder().initChildBuilderParameters(p));
- }
- /**
- * Initializes the parameter objects with data stored in the current bean declaration. This method is called before the
- * newly created builder instance is configured with the parameter objects. It maps attributes of the bean declaration
- * to properties of parameter objects. In addition, it invokes the parent {@code CombinedConfigurationBuilder} so that
- * the parameters object can inherit properties already defined for this builder.
- *
- * @param decl the current {@code ConfigurationDeclaration}
- * @param params the collection with (uninitialized) parameter objects
- * @throws Exception if an error occurs
- */
- protected void initializeParameterObjects(final ConfigurationDeclaration decl, final Collection<BuilderParameters> params) throws Exception {
- inheritParentBuilderProperties(decl, params);
- final MultiWrapDynaBean wrapBean = new MultiWrapDynaBean(params);
- decl.getConfigurationBuilder().initBean(wrapBean, decl);
- }
- /**
- * Determines the <em>allowFailOnInit</em> flag for the newly created builder based on the given
- * {@code ConfigurationDeclaration}. Some combinations of flags in the declaration say that a configuration source is
- * optional, but an empty instance should be created if its creation fail.
- *
- * @param decl the current {@code ConfigurationDeclaration}
- * @return the value of the <em>allowFailOnInit</em> flag
- */
- protected boolean isAllowFailOnInit(final ConfigurationDeclaration decl) {
- return decl.isOptional() && decl.isForceCreate();
- }
- }