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