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