    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 */
    017
    018package org.apache.commons.configuration2.spring;
    019
    020import java.net.URL;
    021import java.util.Properties;
    022
    023import org.apache.commons.configuration2.CompositeConfiguration;
    024import org.apache.commons.configuration2.Configuration;
    025import org.apache.commons.configuration2.ConfigurationConverter;
    026import org.apache.commons.configuration2.builder.fluent.Configurations;
    027import org.apache.commons.lang3.ArrayUtils;
    028import org.springframework.beans.factory.FactoryBean;
    029import org.springframework.beans.factory.InitializingBean;
    030import org.springframework.core.io.Resource;
    031import org.springframework.util.Assert;
    032
    033/**
    034 * <p>FactoryBean which wraps a Commons CompositeConfiguration object for usage
    035 * with PropertiesLoaderSupport. This allows the compositeConfiguration object to behave
    036 * like a normal java.util.Properties object which can be passed on to
    037 * setProperties() method allowing PropertyOverrideConfigurer and
    038 * PropertyPlaceholderConfigurer to take advantage of Commons Configuration.
    039 * </p>
    040 * <p>Internally a CompositeConfiguration object is used for merging multiple
    041 * Configuration objects.</p>
    042 *
    043 * @see java.util.Properties
    044 * @see org.springframework.core.io.support.PropertiesLoaderSupport
    045 *
    046 */
    047public class ConfigurationPropertiesFactoryBean implements InitializingBean, FactoryBean<Properties>
    048{
    049
    050    /** internal CompositeConfiguration containing the merged configuration objects **/
    051    private CompositeConfiguration compositeConfiguration;
    052
    053    /** supplied configurations that will be merged in compositeConfiguration **/
    054    private Configuration[] configurations;
    055
    056    /** Spring resources for loading configurations **/
    057    private Resource[] locations;
    058
    059    /** @see org.apache.commons.configuration2.AbstractConfiguration#throwExceptionOnMissing **/
    060    private boolean throwExceptionOnMissing = true;
    061
    062    public ConfigurationPropertiesFactoryBean()
    063    {
    064    }
    065
    066    public ConfigurationPropertiesFactoryBean(final Configuration configuration)
    067    {
    068        Assert.notNull(configuration, "configuration");
    069        this.compositeConfiguration = new CompositeConfiguration(configuration);
    070    }
    071
    072    /**
    073     * @see org.springframework.beans.factory.FactoryBean#getObject()
    074     */
    075    @Override
    076    public Properties getObject() throws Exception
    077    {
    078        return (compositeConfiguration != null) ? ConfigurationConverter.getProperties(compositeConfiguration) : null;
    079    }
    080
    081    /**
    082     * @see org.springframework.beans.factory.FactoryBean#getObjectType()
    083     */
    084    @Override
    085    public Class<?> getObjectType()
    086    {
    087        return java.util.Properties.class;
    088    }
    089
    090    /**
    091     * @see org.springframework.beans.factory.FactoryBean#isSingleton()
    092     */
    093    @Override
    094    public boolean isSingleton()
    095    {
    096        return true;
    097    }
    098
    099    /**
    100     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
    101     */
    102    @Override
    103    public void afterPropertiesSet() throws Exception
    104    {
    105        if (compositeConfiguration == null && ArrayUtils.isEmpty(configurations) && ArrayUtils.isEmpty(locations))
    106        {
    107            throw new IllegalArgumentException("no configuration object or location specified");
    108        }
    109
    110        if (compositeConfiguration == null)
    111        {
    112            compositeConfiguration = new CompositeConfiguration();
    113        }
    114
    115        compositeConfiguration.setThrowExceptionOnMissing(throwExceptionOnMissing);
    116
    117        if (configurations != null)
    118        {
    119            for (final Configuration configuration : configurations)
    120            {
    121                compositeConfiguration.addConfiguration(configuration);
    122            }
    123        }
    124
    125        if (locations != null)
    126        {
    127            for (final Resource location : locations)
    128            {
    129                final URL url = location.getURL();
    130                final Configuration props = new Configurations().properties(url);
    131                compositeConfiguration.addConfiguration(props);
    132            }
    133        }
    134    }
    135
    136    public Configuration[] getConfigurations()
    137    {
    138        return defensiveCopy(configurations);
    139    }
    140
    141    /**
    142     * Set the commons configurations objects which will be used as properties.
    143     *
    144     * @param configurations commons configurations objects which will be used as properties.
    145     */
    146    public void setConfigurations(final Configuration[] configurations)
    147    {
    148        this.configurations = defensiveCopy(configurations);
    149    }
    150
    151    public Resource[] getLocations()
    152    {
    153        return defensiveCopy(locations);
    154    }
    155
    156    /**
    157     * Shortcut for loading compositeConfiguration from Spring resources. It will
    158     * internally create a PropertiesConfiguration object based on the URL
    159     * retrieved from the given Resources.
    160     *
    161     * @param locations resources of configuration files
    162     */
    163    public void setLocations(final Resource[] locations)
    164    {
    165        this.locations = defensiveCopy(locations);
    166    }
    167
    168    public boolean isThrowExceptionOnMissing()
    169    {
    170        return throwExceptionOnMissing;
    171    }
    172
    173    /**
    174     * Set the underlying Commons CompositeConfiguration throwExceptionOnMissing flag.
    175     *
    176     * @see org.apache.commons.configuration2.AbstractConfiguration#setThrowExceptionOnMissing(boolean)
    177     * @param throwExceptionOnMissing The new value for the property
    178     */
    179    public void setThrowExceptionOnMissing(final boolean throwExceptionOnMissing)
    180    {
    181        this.throwExceptionOnMissing = throwExceptionOnMissing;
    182    }
    183
    184    public CompositeConfiguration getConfiguration()
    185    {
    186        return compositeConfiguration;
    187    }
    188
    189    /**
    190     * Creates a defensive copy of the specified array. Handles null values
    191     * correctly.
    192     *
    193     * @param src the source array
    194     * @param <T> the type of the array
    195     * @return the defensive copy of the array
    196     */
    197    private static <T> T[] defensiveCopy(final T[] src)
    198    {
    199        return (src != null) ? src.clone() : null;
    200    }
    201}