1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.configuration;
24  
25  import com.germinus.easyconf.AggregatedProperties;
26  import com.germinus.easyconf.ComponentConfiguration;
27  import com.germinus.easyconf.ComponentProperties;
28  import com.germinus.easyconf.EasyConf;
29  
30  import com.liferay.portal.kernel.configuration.Filter;
31  import com.liferay.portal.kernel.util.Validator;
32  
33  import java.lang.reflect.Field;
34  
35  import java.net.URI;
36  import java.net.URISyntaxException;
37  import java.net.URL;
38  
39  import java.util.HashSet;
40  import java.util.Iterator;
41  import java.util.List;
42  import java.util.Map;
43  import java.util.Properties;
44  import java.util.Set;
45  
46  import org.apache.commons.configuration.CompositeConfiguration;
47  import org.apache.commons.configuration.Configuration;
48  import org.apache.commons.configuration.MapConfiguration;
49  import org.apache.commons.logging.Log;
50  import org.apache.commons.logging.LogFactory;
51  
52  /**
53   * <a href="ConfigurationImpl.java.html"><b><i>View Source</i></b></a>
54   *
55   * @author Brian Wing Shun Chan
56   *
57   */
58  public class ConfigurationImpl
59      implements com.liferay.portal.kernel.configuration.Configuration {
60  
61      public void addProperties(Properties properties) {
62          try {
63              ComponentProperties componentProperties =
64                  _componentConfiguration.getProperties();
65  
66              AggregatedProperties aggregatedProperties =
67                  (AggregatedProperties)componentProperties.toConfiguration();
68  
69              Field field1 = CompositeConfiguration.class.getDeclaredField(
70                  "configList");
71  
72              field1.setAccessible(true);
73  
74              // Add to configList of base conf
75  
76              List<Configuration> configurations =
77                  (List<Configuration>)field1.get(aggregatedProperties);
78  
79              MapConfiguration newConfiguration =
80                  new MapConfiguration(properties);
81  
82              configurations.add(0, newConfiguration);
83  
84              // Add to configList of AggregatedProperties itself
85  
86              Field field2 = aggregatedProperties.getClass().getDeclaredField(
87                  "baseConf");
88  
89              field2.setAccessible(true);
90  
91              CompositeConfiguration compositeConfiguration =
92                  (CompositeConfiguration)field2.get(aggregatedProperties);
93  
94              configurations = (List<Configuration>)field1.get(
95                  compositeConfiguration);
96  
97              configurations.add(0, newConfiguration);
98          }
99          catch (Exception e) {
100             _log.error("The properties could not be added", e);
101         }
102     }
103 
104     public boolean contains(String key) {
105         return getComponentProperties().containsKey(key);
106     }
107 
108     public String get(String key) {
109         if (_PRINT_DUPLICATE_CALLS_TO_GET) {
110             if (_keys.contains(key)) {
111                 System.out.println("Duplicate call to get " + key);
112             }
113             else {
114                 _keys.add(key);
115             }
116         }
117 
118         return getComponentProperties().getString(key);
119     }
120 
121     public String get(String key, Filter filter) {
122         return getComponentProperties().getString(
123             key, getEasyConfFilter(filter));
124     }
125 
126     public String[] getArray(String key) {
127         String[] array = getComponentProperties().getStringArray(key);
128 
129         if (array == null) {
130             return new String[0];
131         }
132         else if (array.length > 0) {
133 
134             // Commons Configuration parses an empty property into a String
135             // array with one String containing one space. It also leaves a
136             // trailing array member if you set a property in more than one
137             // line.
138 
139             if (Validator.isNull(array[array.length - 1])) {
140                 String[] subArray = new String[array.length - 1];
141 
142                 System.arraycopy(array, 0, subArray, 0, subArray.length);
143 
144                 array = subArray;
145             }
146         }
147 
148         return array;
149     }
150 
151     public String[] getArray(String key, Filter filter) {
152         return getComponentProperties().getStringArray(
153             key, getEasyConfFilter(filter));
154     }
155 
156     public Properties getProperties() {
157 
158         // For some strange reason, componentProperties.getProperties() returns
159         // values with spaces after commas. So a property setting of "xyz=1,2,3"
160         // actually returns "xyz=1, 2, 3". This can break applications that
161         // don't expect that extra space. However, getting the property value
162         // directly through componentProperties returns the correct value. This
163         // method fixes the weird behavior by returing properties with the
164         // correct values.
165 
166         Properties properties = new Properties();
167 
168         ComponentProperties componentProperties = getComponentProperties();
169 
170         Iterator<Map.Entry<Object, Object>> itr =
171             componentProperties.getProperties().entrySet().iterator();
172 
173         while (itr.hasNext()) {
174             Map.Entry<Object, Object> entry = itr.next();
175 
176             String key = (String)entry.getKey();
177             String value = (String)entry.getValue();
178 
179             properties.setProperty(key, value);
180         }
181 
182         return properties;
183     }
184 
185     public void removeProperties(Properties properties) {
186         try {
187             ComponentProperties componentProperties =
188                 _componentConfiguration.getProperties();
189 
190             AggregatedProperties aggregatedProperties =
191                 (AggregatedProperties)componentProperties.toConfiguration();
192 
193             Field field1 = aggregatedProperties.getClass().getDeclaredField(
194                 "baseConf");
195 
196             field1.setAccessible(true);
197 
198             CompositeConfiguration compositeConfiguration =
199                 (CompositeConfiguration)field1.get(aggregatedProperties);
200 
201             Field field2 = CompositeConfiguration.class.getDeclaredField(
202                 "configList");
203 
204             field2.setAccessible(true);
205 
206             List<Configuration> configurations =
207                 (List<Configuration>)field2.get(compositeConfiguration);
208 
209             Iterator<Configuration> itr = configurations.iterator();
210 
211             while (itr.hasNext()) {
212                 Configuration configuration = itr.next();
213 
214                 if (!(configuration instanceof MapConfiguration)) {
215                     return;
216                 }
217 
218                 MapConfiguration mapConfiguration =
219                     (MapConfiguration)configuration;
220 
221                 if (mapConfiguration.getMap() == properties) {
222                     itr.remove();
223 
224                     aggregatedProperties.removeConfiguration(configuration);
225                 }
226             }
227         }
228         catch (Exception e) {
229             _log.error("The properties could not be removed", e);
230         }
231     }
232 
233     public void set(String key, String value) {
234         getComponentProperties().setProperty(key, value);
235     }
236 
237     protected ConfigurationImpl(ClassLoader classLoader, String name) {
238         _componentConfiguration = EasyConf.getConfiguration(
239             getFileName(classLoader, name));
240 
241         printSources();
242     }
243 
244     protected ComponentProperties getComponentProperties() {
245         return _componentConfiguration.getProperties();
246     }
247 
248     protected com.germinus.easyconf.Filter getEasyConfFilter(Filter filter) {
249         com.germinus.easyconf.Filter easyConfFilter =
250             com.germinus.easyconf.Filter.by(filter.getSelectors());
251 
252         if (filter.getVariables() != null) {
253             easyConfFilter.setVariables(filter.getVariables());
254         }
255 
256         return easyConfFilter;
257     }
258 
259     protected String getFileName(ClassLoader classLoader, String name) {
260         URL url = classLoader.getResource(name + ".properties");
261 
262         // If the resource is located inside of a .jar, then
263         // EasyConf needs the jar:file: prefix appended to
264         // the path.  Use URL.toExternalForm() to achieve that.
265 
266         if (url.getProtocol().equals("jar")) {
267             name = url.toExternalForm();
268         }
269         else {
270             try {
271                 name = new URI(url.getPath()).getPath();
272             }
273             catch (URISyntaxException urise) {
274                 name = url.getFile();
275             }
276         }
277 
278         int pos = name.lastIndexOf(".properties");
279 
280         if (pos != -1) {
281             name = name.substring(0, pos);
282         }
283 
284         return name;
285     }
286 
287     protected void printSources() {
288         List<String> sources = getComponentProperties().getLoadedSources();
289 
290         for (int i = sources.size() - 1; i >= 0; i--) {
291             String source = sources.get(i);
292 
293             System.out.println("Loading " + source);
294         }
295     }
296 
297     private static final boolean _PRINT_DUPLICATE_CALLS_TO_GET = false;
298 
299     private static Log _log = LogFactory.getLog(ConfigurationImpl.class);
300 
301     private ComponentConfiguration _componentConfiguration;
302     private Set<String> _keys = new HashSet<String>();
303 
304 }