View Javadoc

1   /*
2    * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.properties.spi.config.support;
17  
18  import java.io.BufferedInputStream;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.net.URL;
22  import java.net.URLClassLoader;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.Enumeration;
26  import java.util.List;
27  import java.util.Properties;
28  import java.util.Set;
29  
30  import javax.annotation.concurrent.NotThreadSafe;
31  
32  import org.apache.commons.io.IOUtils;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  
36  import de.smartics.properties.api.config.app.BootProperties;
37  import de.smartics.properties.api.config.domain.CompoundConfigurationException;
38  import de.smartics.properties.api.config.domain.ConfigurationException;
39  import de.smartics.properties.api.config.domain.ConfigurationPropertiesManagement;
40  import de.smartics.properties.api.config.domain.ConfigurationValidationException;
41  import de.smartics.properties.api.config.domain.Property;
42  import de.smartics.properties.api.config.domain.PropertyLocation;
43  import de.smartics.properties.api.config.domain.PropertyProvider;
44  import de.smartics.properties.api.core.domain.PropertiesContext;
45  import de.smartics.properties.api.core.domain.PropertyDescriptor;
46  import de.smartics.properties.spi.core.classpath.PropertiesFilesLoader;
47  import de.smartics.properties.spi.core.metadata.PropertyMetaDataParser;
48  import de.smartics.util.lang.Arg;
49  import de.smartics.util.lang.NullArgumentException;
50  
51  /**
52   * Loads boot properties that influence the loading of properties.
53   */
54  @NotThreadSafe
55  public final class BootLoader
56  {
57    // ********************************* Fields *********************************
58  
59    // --- constants ------------------------------------------------------------
60  
61    /**
62     * Reference to the logger for this class.
63     */
64    private static final Logger LOG = LoggerFactory.getLogger(BootLoader.class);
65  
66    // --- members --------------------------------------------------------------
67  
68    /**
69     * The configuration to add properties to.
70     */
71    private final ConfigurationPropertiesManagement configuration;
72  
73    /**
74     * The class root URLs to search for property descriptors and properties
75     * files.
76     */
77    private final Collection<URL> rootUrls;
78  
79    // ****************************** Initializer *******************************
80  
81    // ****************************** Constructors ******************************
82  
83    /**
84     * Convenience constructor to initialize class path root URLs.
85     *
86     * @param configuration the configuration to add properties to.
87     * @param classLoader the class loader whose class roots are added.
88     * @throws NullArgumentException if {@code configuration} or {@code rootUrls}
89     *           is <code>null</code>.
90     */
91    public BootLoader(final ConfigurationPropertiesManagement configuration,
92        final ClassLoader classLoader) throws NullArgumentException
93    {
94      this.configuration = Arg.checkNotNull("configuration", configuration);
95      this.rootUrls = readRootUrls(Arg.checkNotNull("classLoader", classLoader));
96    }
97  
98    // ****************************** Inner Classes *****************************
99  
100   // ********************************* Methods ********************************
101 
102   // --- init -----------------------------------------------------------------
103 
104   private static List<URL> readRootUrls(final ClassLoader classLoader)
105     throws NullArgumentException
106   {
107     final List<URL> list = new ArrayList<URL>();
108     try
109     {
110       final Enumeration<URL> rootUrls = classLoader.getResources("");
111       while (rootUrls.hasMoreElements())
112       {
113         final URL rootUrl = rootUrls.nextElement();
114         list.add(rootUrl);
115       }
116     }
117     catch (final IOException e)
118     {
119       LOG.warn("Cannot determine class path roots for the given class loader.",
120           e);
121     }
122 
123     return list;
124   }
125 
126   // --- get&set --------------------------------------------------------------
127 
128   // --- business -------------------------------------------------------------
129 
130   /**
131    * Loads the configuration properties instance from information found on the
132    * class path.
133    *
134    * @return the loaded configuration properties instance.
135    * @throws CompoundConfigurationException if loading encountered problems.
136    */
137   public ConfigurationPropertiesManagement load()
138     throws CompoundConfigurationException
139   {
140     final MultiSourceProperties properties = loadProperties();
141 
142     final List<ConfigurationException> exceptions = properties.getExceptions();
143     if (!exceptions.isEmpty())
144     {
145       throw new CompoundConfigurationException(configuration.getKey(),
146           exceptions);
147     }
148 
149     addProperties(properties);
150 
151     return configuration;
152   }
153 
154   /**
155    * Loads the configuration properties instance from information found on the
156    * class path.
157    *
158    * @return the loaded configuration properties instance.
159    * @throws ConfigurationValidationException if the validation of the
160    *           configuration failed.
161    */
162   public ConfigurationPropertiesManagement loadAndValidate()
163     throws ConfigurationValidationException
164   {
165     load();
166     configuration.validate(true);
167 
168     return configuration;
169   }
170 
171   private MultiSourceProperties loadProperties()
172   {
173     final MultiSourceProperties allProperties =
174         new MultiSourceProperties(configuration.getKey(),
175             new ArrayList<ConfigurationException>());
176 
177     final PropertiesFilesLoader loader = new PropertiesFilesLoader();
178     final Set<String> propertiesFiles = loader.getBootPropertiesFiles(rootUrls);
179 
180     final ClassLoader classLoader =
181         new URLClassLoader(rootUrls.toArray(new URL[rootUrls.size()]), Thread
182             .currentThread().getContextClassLoader());
183     for (final String propertiesFile : propertiesFiles)
184     {
185       final Properties properties = loadProperties(classLoader, propertiesFile);
186       final PropertyLocation location =
187           new PropertyLocationHelper().createPropertyLocation(classLoader,
188               propertiesFile);
189       allProperties.add(location, properties);
190     }
191 
192     return allProperties;
193   }
194 
195   private Properties loadProperties(final ClassLoader classLoader,
196       final String propertiesFile)
197   {
198     // TODO: There is a similar method in PropertiesUtils: Refactor
199     final Properties properties = new Properties();
200     InputStream in = classLoader.getResourceAsStream(propertiesFile);
201     try
202     {
203       if (in != null)
204       {
205         in = new BufferedInputStream(in);
206         properties.load(in);
207       }
208       else
209       {
210         LOG.warn("Cannot find properties '" + propertiesFile
211                  + "' in class path.");
212       }
213     }
214     catch (final IOException e)
215     {
216       LOG.warn("Cannot load properties from '" + propertiesFile + "'.");
217     }
218     finally
219     {
220       IOUtils.closeQuietly(in);
221     }
222 
223     return properties;
224   }
225 
226   private void addProperties(final MultiSourceProperties compositeProperties)
227   {
228     final Properties properties = new Properties();
229     final Class<?> type = BootProperties.class;
230 
231     final PropertiesContext context = configuration.getContext(type);
232     final PropertyMetaDataParser propertyDescriptorParser =
233         PropertyMetaDataParser.create(context);
234 
235     final List<PropertyDescriptor> descriptors =
236         propertyDescriptorParser.readDescriptors(type);
237 
238     for (final PropertyDescriptor descriptor : descriptors)
239     {
240       final String propertyKey = descriptor.getKey().toString();
241       final Property property = compositeProperties.getValue(propertyKey);
242       if (property != null && property.getValue() != null)
243       {
244         properties.put(propertyKey, property);
245       }
246       else
247       {
248         properties.remove(propertyKey);
249       }
250     }
251 
252     configuration.addDescriptors(type);
253     final PropertyProvider provider =
254         new PropertiesPropertyProvider(configuration.getKey(),
255             new PropertyLocation("boot-various"), properties);
256     configuration.addDefinitions(provider);
257   }
258 
259   // --- object basics --------------------------------------------------------
260 
261 }