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