View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.configuration2.spring;
19  
20  import java.util.Properties;
21  import java.util.stream.Stream;
22  
23  import org.apache.commons.configuration2.CompositeConfiguration;
24  import org.apache.commons.configuration2.Configuration;
25  import org.apache.commons.configuration2.ConfigurationConverter;
26  import org.apache.commons.configuration2.builder.fluent.Configurations;
27  import org.apache.commons.lang3.ArrayUtils;
28  import org.springframework.beans.factory.FactoryBean;
29  import org.springframework.beans.factory.InitializingBean;
30  import org.springframework.core.io.Resource;
31  import org.springframework.util.Assert;
32  
33  /**
34   * <p>
35   * FactoryBean which wraps a Commons CompositeConfiguration object for usage with PropertiesLoaderSupport. This allows
36   * the compositeConfiguration object to behave like a normal {@link Properties} object which can be passed on to
37   * setProperties() method allowing PropertyOverrideConfigurer and PropertyPlaceholderConfigurer to take advantage of
38   * Commons Configuration.
39   * </p>
40   * <p>
41   * Internally a CompositeConfiguration object is used for merging multiple Configuration objects.
42   * </p>
43   *
44   * @see java.util.Properties
45   * @see org.springframework.core.io.support.PropertiesLoaderSupport
46   */
47  public class ConfigurationPropertiesFactoryBean implements InitializingBean, FactoryBean<Properties> {
48  
49      /**
50       * Creates a defensive copy of the specified array. Handles null values correctly.
51       *
52       * @param src the source array
53       * @param <T> the type of the array
54       * @return the defensive copy of the array
55       */
56      private static <T> T[] clone(final T[] src) {
57          return src != null ? src.clone() : null;
58      }
59  
60      /** Internal CompositeConfiguration containing the merged configuration objects **/
61      private CompositeConfiguration compositeConfiguration;
62  
63      /** Supplied configurations that will be merged in compositeConfiguration **/
64      private Configuration[] configurations;
65  
66      /** Spring resources for loading configurations **/
67      private Resource[] locations;
68  
69      /** @see org.apache.commons.configuration2.AbstractConfiguration#throwExceptionOnMissing **/
70      private boolean throwExceptionOnMissing = true;
71  
72      /**
73       * Constructs a new instance.
74       */
75      public ConfigurationPropertiesFactoryBean() {
76      }
77  
78      /**
79       * Constructs a new instance.
80       *
81       * @param configuration The configuration to compose.
82       */
83      public ConfigurationPropertiesFactoryBean(final Configuration configuration) {
84          Assert.notNull(configuration, "configuration");
85          this.compositeConfiguration = new CompositeConfiguration(configuration);
86      }
87  
88      /**
89       * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
90       */
91      @Override
92      public void afterPropertiesSet() throws Exception {
93          if (compositeConfiguration == null && ArrayUtils.isEmpty(configurations) && ArrayUtils.isEmpty(locations)) {
94              throw new IllegalArgumentException("no configuration object or location specified");
95          }
96  
97          if (compositeConfiguration == null) {
98              compositeConfiguration = new CompositeConfiguration();
99          }
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 }