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      /** Internal CompositeConfiguration containing the merged configuration objects **/
50      private CompositeConfiguration compositeConfiguration;
51  
52      /** Supplied configurations that will be merged in compositeConfiguration **/
53      private Configuration[] configurations;
54  
55      /** Spring resources for loading configurations **/
56      private Resource[] locations;
57  
58      /** @see org.apache.commons.configuration2.AbstractConfiguration#throwExceptionOnMissing **/
59      private boolean throwExceptionOnMissing = true;
60  
61      public ConfigurationPropertiesFactoryBean() {
62      }
63  
64      public ConfigurationPropertiesFactoryBean(final Configuration configuration) {
65          Assert.notNull(configuration, "configuration");
66          this.compositeConfiguration = new CompositeConfiguration(configuration);
67      }
68  
69      /**
70       * @see org.springframework.beans.factory.FactoryBean#getObject()
71       */
72      @Override
73      public Properties getObject() throws Exception {
74          return compositeConfiguration != null ? ConfigurationConverter.getProperties(compositeConfiguration) : null;
75      }
76  
77      /**
78       * @see org.springframework.beans.factory.FactoryBean#getObjectType()
79       */
80      @Override
81      public Class<?> getObjectType() {
82          return Properties.class;
83      }
84  
85      /**
86       * @see org.springframework.beans.factory.FactoryBean#isSingleton()
87       */
88      @Override
89      public boolean isSingleton() {
90          return true;
91      }
92  
93      /**
94       * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
95       */
96      @Override
97      public void afterPropertiesSet() throws Exception {
98          if (compositeConfiguration == null && ArrayUtils.isEmpty(configurations) && ArrayUtils.isEmpty(locations)) {
99              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 }