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 /** Internal CompositeConfiguration containing the merged configuration objects **/ 050 private CompositeConfiguration compositeConfiguration; 051 052 /** Supplied configurations that will be merged in compositeConfiguration **/ 053 private Configuration[] configurations; 054 055 /** Spring resources for loading configurations **/ 056 private Resource[] locations; 057 058 /** @see org.apache.commons.configuration2.AbstractConfiguration#throwExceptionOnMissing **/ 059 private boolean throwExceptionOnMissing = true; 060 061 public ConfigurationPropertiesFactoryBean() { 062 } 063 064 public ConfigurationPropertiesFactoryBean(final Configuration configuration) { 065 Assert.notNull(configuration, "configuration"); 066 this.compositeConfiguration = new CompositeConfiguration(configuration); 067 } 068 069 /** 070 * @see org.springframework.beans.factory.FactoryBean#getObject() 071 */ 072 @Override 073 public Properties getObject() throws Exception { 074 return compositeConfiguration != null ? ConfigurationConverter.getProperties(compositeConfiguration) : null; 075 } 076 077 /** 078 * @see org.springframework.beans.factory.FactoryBean#getObjectType() 079 */ 080 @Override 081 public Class<?> getObjectType() { 082 return Properties.class; 083 } 084 085 /** 086 * @see org.springframework.beans.factory.FactoryBean#isSingleton() 087 */ 088 @Override 089 public boolean isSingleton() { 090 return true; 091 } 092 093 /** 094 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() 095 */ 096 @Override 097 public void afterPropertiesSet() throws Exception { 098 if (compositeConfiguration == null && ArrayUtils.isEmpty(configurations) && ArrayUtils.isEmpty(locations)) { 099 throw new IllegalArgumentException("no configuration object or location specified"); 100 } 101 102 if (compositeConfiguration == null) { 103 compositeConfiguration = new CompositeConfiguration(); 104 } 105 106 compositeConfiguration.setThrowExceptionOnMissing(throwExceptionOnMissing); 107 108 if (configurations != null) { 109 Stream.of(configurations).forEach(compositeConfiguration::addConfiguration); 110 } 111 112 if (locations != null) { 113 for (final Resource location : locations) { 114 compositeConfiguration.addConfiguration(new Configurations().properties(location.getURL())); 115 } 116 } 117 } 118 119 public Configuration[] getConfigurations() { 120 return defensiveCopy(configurations); 121 } 122 123 /** 124 * Sets the commons configurations objects which will be used as properties. 125 * 126 * @param configurations commons configurations objects which will be used as properties. 127 */ 128 public void setConfigurations(final Configuration... configurations) { 129 this.configurations = defensiveCopy(configurations); 130 } 131 132 public Resource[] getLocations() { 133 return defensiveCopy(locations); 134 } 135 136 /** 137 * Shortcut for loading compositeConfiguration from Spring resources. It will internally create a 138 * PropertiesConfiguration object based on the URL retrieved from the given Resources. 139 * 140 * @param locations resources of configuration files 141 */ 142 public void setLocations(final Resource... locations) { 143 this.locations = defensiveCopy(locations); 144 } 145 146 public boolean isThrowExceptionOnMissing() { 147 return throwExceptionOnMissing; 148 } 149 150 /** 151 * Sets the underlying Commons CompositeConfiguration throwExceptionOnMissing flag. 152 * 153 * @see org.apache.commons.configuration2.AbstractConfiguration#setThrowExceptionOnMissing(boolean) 154 * @param throwExceptionOnMissing The new value for the property 155 */ 156 public void setThrowExceptionOnMissing(final boolean throwExceptionOnMissing) { 157 this.throwExceptionOnMissing = throwExceptionOnMissing; 158 } 159 160 public CompositeConfiguration getConfiguration() { 161 return compositeConfiguration; 162 } 163 164 /** 165 * Creates a defensive copy of the specified array. Handles null values correctly. 166 * 167 * @param src the source array 168 * @param <T> the type of the array 169 * @return the defensive copy of the array 170 */ 171 private static <T> T[] defensiveCopy(final T[] src) { 172 return src != null ? src.clone() : null; 173 } 174}