1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.configuration;
16  
17  import com.germinus.easyconf.AggregatedProperties;
18  import com.germinus.easyconf.ComponentConfiguration;
19  import com.germinus.easyconf.ComponentProperties;
20  import com.germinus.easyconf.Conventions;
21  import com.germinus.easyconf.EasyConf;
22  
23  import com.liferay.portal.kernel.configuration.Filter;
24  import com.liferay.portal.kernel.log.Log;
25  import com.liferay.portal.kernel.log.LogFactoryUtil;
26  import com.liferay.portal.kernel.util.PropertiesUtil;
27  import com.liferay.portal.kernel.util.StringPool;
28  import com.liferay.portal.kernel.util.StringUtil;
29  import com.liferay.portal.kernel.util.Validator;
30  import com.liferay.portal.model.Company;
31  import com.liferay.portal.model.CompanyConstants;
32  import com.liferay.portal.service.CompanyLocalServiceUtil;
33  
34  import java.io.FileWriter;
35  import java.io.Writer;
36  
37  import java.lang.reflect.Field;
38  
39  import java.net.URI;
40  import java.net.URISyntaxException;
41  import java.net.URL;
42  
43  import java.util.HashSet;
44  import java.util.Iterator;
45  import java.util.List;
46  import java.util.Map;
47  import java.util.Properties;
48  import java.util.Set;
49  
50  import org.apache.commons.configuration.CompositeConfiguration;
51  import org.apache.commons.configuration.Configuration;
52  import org.apache.commons.configuration.MapConfiguration;
53  
54  /**
55   * <a href="ConfigurationImpl.java.html"><b><i>View Source</i></b></a>
56   *
57   * @author Brian Wing Shun Chan
58   */
59  public class ConfigurationImpl
60      implements com.liferay.portal.kernel.configuration.Configuration {
61  
62      public ConfigurationImpl(ClassLoader classLoader, String name) {
63          this(classLoader, name, CompanyConstants.SYSTEM);
64      }
65  
66      public ConfigurationImpl(
67          ClassLoader classLoader, String name, long companyId) {
68  
69          try {
70              URL url = classLoader.getResource(
71                  name + Conventions.PROPERTIES_EXTENSION);
72  
73              if ((url != null) && url.getProtocol().equals("file")) {
74                  String basePath = url.getPath();
75  
76                  int pos = name.lastIndexOf(
77                      StringPool.SLASH + name + Conventions.PROPERTIES_EXTENSION);
78  
79                  if (pos != -1) {
80                      basePath = basePath.substring(0, pos);
81                  }
82  
83                  Properties properties = new Properties();
84  
85                  properties.load(url.openStream());
86  
87                  if (!properties.containsKey("base.path")) {
88                      String fileName = StringUtil.replace(
89                          url.getFile(), "%20", StringPool.SPACE);
90  
91                      Writer writer = new FileWriter(fileName, true);
92  
93                      writer.write("\n\nbase.path=".concat(basePath));
94  
95                      writer.close();
96                  }
97              }
98          }
99          catch (Exception e) {
100             _log.error(e, e);
101         }
102 
103         String webId = null;
104 
105         if (companyId > CompanyConstants.SYSTEM) {
106             try {
107                 Company company = CompanyLocalServiceUtil.getCompanyById(
108                     companyId);
109 
110                 webId = company.getWebId();
111             }
112             catch (Exception e) {
113                 _log.error(e, e);
114             }
115         }
116 
117         if (webId != null) {
118             _componentConfiguration = EasyConf.getConfiguration(
119                 webId, getFileName(classLoader, name));
120         }
121         else {
122             _componentConfiguration = EasyConf.getConfiguration(
123                 getFileName(classLoader, name));
124         }
125 
126         printSources(companyId, webId);
127     }
128 
129     public void addProperties(Properties properties) {
130         try {
131             ComponentProperties componentProperties =
132                 _componentConfiguration.getProperties();
133 
134             AggregatedProperties aggregatedProperties =
135                 (AggregatedProperties)componentProperties.toConfiguration();
136 
137             Field field1 = CompositeConfiguration.class.getDeclaredField(
138                 "configList");
139 
140             field1.setAccessible(true);
141 
142             // Add to configList of base conf
143 
144             List<Configuration> configurations =
145                 (List<Configuration>)field1.get(aggregatedProperties);
146 
147             MapConfiguration newConfiguration =
148                 new MapConfiguration(properties);
149 
150             configurations.add(0, newConfiguration);
151 
152             // Add to configList of AggregatedProperties itself
153 
154             Field field2 = aggregatedProperties.getClass().getDeclaredField(
155                 "baseConf");
156 
157             field2.setAccessible(true);
158 
159             CompositeConfiguration compositeConfiguration =
160                 (CompositeConfiguration)field2.get(aggregatedProperties);
161 
162             configurations = (List<Configuration>)field1.get(
163                 compositeConfiguration);
164 
165             configurations.add(0, newConfiguration);
166         }
167         catch (Exception e) {
168             _log.error("The properties could not be added", e);
169         }
170     }
171 
172     public boolean contains(String key) {
173         return getComponentProperties().containsKey(key);
174     }
175 
176     public String get(String key) {
177         if (_PRINT_DUPLICATE_CALLS_TO_GET) {
178             if (_keys.contains(key)) {
179                 System.out.println("Duplicate call to get " + key);
180             }
181             else {
182                 _keys.add(key);
183             }
184         }
185 
186         return getComponentProperties().getString(key);
187     }
188 
189     public String get(String key, Filter filter) {
190         return getComponentProperties().getString(
191             key, getEasyConfFilter(filter));
192     }
193 
194     public String[] getArray(String key) {
195         String[] array = getComponentProperties().getStringArray(key);
196 
197         if (array == null) {
198             return new String[0];
199         }
200         else if (array.length > 0) {
201 
202             // Commons Configuration parses an empty property into a String
203             // array with one String containing one space. It also leaves a
204             // trailing array member if you set a property in more than one
205             // line.
206 
207             if (Validator.isNull(array[array.length - 1])) {
208                 String[] subArray = new String[array.length - 1];
209 
210                 System.arraycopy(array, 0, subArray, 0, subArray.length);
211 
212                 array = subArray;
213             }
214         }
215 
216         return array;
217     }
218 
219     public String[] getArray(String key, Filter filter) {
220         return getComponentProperties().getStringArray(
221             key, getEasyConfFilter(filter));
222     }
223 
224     public Properties getProperties() {
225 
226         // For some strange reason, componentProperties.getProperties() returns
227         // values with spaces after commas. So a property setting of "xyz=1,2,3"
228         // actually returns "xyz=1, 2, 3". This can break applications that
229         // don't expect that extra space. However, getting the property value
230         // directly through componentProperties returns the correct value. This
231         // method fixes the weird behavior by returing properties with the
232         // correct values.
233 
234         Properties properties = new Properties();
235 
236         ComponentProperties componentProperties = getComponentProperties();
237 
238         Iterator<Map.Entry<Object, Object>> itr =
239             componentProperties.getProperties().entrySet().iterator();
240 
241         while (itr.hasNext()) {
242             Map.Entry<Object, Object> entry = itr.next();
243 
244             String key = (String)entry.getKey();
245             String value = (String)entry.getValue();
246 
247             properties.setProperty(key, value);
248         }
249 
250         return properties;
251     }
252 
253     public Properties getProperties(String prefix, boolean removePrefix) {
254         Properties allProperties = getProperties();
255 
256         return PropertiesUtil.getProperties(
257             allProperties, prefix, removePrefix);
258     }
259 
260     public void removeProperties(Properties properties) {
261         try {
262             ComponentProperties componentProperties =
263                 _componentConfiguration.getProperties();
264 
265             AggregatedProperties aggregatedProperties =
266                 (AggregatedProperties)componentProperties.toConfiguration();
267 
268             Field field1 = aggregatedProperties.getClass().getDeclaredField(
269                 "baseConf");
270 
271             field1.setAccessible(true);
272 
273             CompositeConfiguration compositeConfiguration =
274                 (CompositeConfiguration)field1.get(aggregatedProperties);
275 
276             Field field2 = CompositeConfiguration.class.getDeclaredField(
277                 "configList");
278 
279             field2.setAccessible(true);
280 
281             List<Configuration> configurations =
282                 (List<Configuration>)field2.get(compositeConfiguration);
283 
284             Iterator<Configuration> itr = configurations.iterator();
285 
286             while (itr.hasNext()) {
287                 Configuration configuration = itr.next();
288 
289                 if (!(configuration instanceof MapConfiguration)) {
290                     return;
291                 }
292 
293                 MapConfiguration mapConfiguration =
294                     (MapConfiguration)configuration;
295 
296                 if (mapConfiguration.getMap() == properties) {
297                     itr.remove();
298 
299                     aggregatedProperties.removeConfiguration(configuration);
300                 }
301             }
302         }
303         catch (Exception e) {
304             _log.error("The properties could not be removed", e);
305         }
306     }
307 
308     public void set(String key, String value) {
309         getComponentProperties().setProperty(key, value);
310     }
311 
312     protected ComponentProperties getComponentProperties() {
313         return _componentConfiguration.getProperties();
314     }
315 
316     protected com.germinus.easyconf.Filter getEasyConfFilter(Filter filter) {
317         com.germinus.easyconf.Filter easyConfFilter =
318             com.germinus.easyconf.Filter.by(filter.getSelectors());
319 
320         if (filter.getVariables() != null) {
321             easyConfFilter.setVariables(filter.getVariables());
322         }
323 
324         return easyConfFilter;
325     }
326 
327     protected String getFileName(ClassLoader classLoader, String name) {
328         URL url = classLoader.getResource(name + ".properties");
329 
330         // If the resource is located inside of a JAR, then EasyConf needs the
331         // "jar:file:" prefix appended to the path. Use URL.toExternalForm() to
332         // achieve that. When running under JBoss, the protocol returned is
333         // "vfsfile" or "vfszip". When running under OC4J, the protocol returned
334         // is "code-source". When running under WebLogic, the protocol returned
335         // is "zip". When running under WebSphere, the protocol returned is
336         // "wsjar".
337 
338         String protocol = url.getProtocol();
339 
340         if (protocol.equals("code-source") || protocol.equals("jar") ||
341             protocol.equals("vfsfile") || protocol.equals("vfszip") ||
342             protocol.equals("wsjar") || protocol.equals("zip")) {
343 
344             name = url.toExternalForm();
345         }
346         else {
347             try {
348                 name = new URI(url.getPath()).getPath();
349             }
350             catch (URISyntaxException urise) {
351                 name = url.getFile();
352             }
353         }
354 
355         int pos = name.lastIndexOf(".properties");
356 
357         if (pos != -1) {
358             name = name.substring(0, pos);
359         }
360 
361         return name;
362     }
363 
364     protected void printSources(long companyId, String webId) {
365         List<String> sources = getComponentProperties().getLoadedSources();
366 
367         for (int i = sources.size() - 1; i >= 0; i--) {
368             String source = sources.get(i);
369 
370             String info = "Loading " + source;
371 
372             if (companyId > CompanyConstants.SYSTEM) {
373                 info +=
374                     " for {companyId=" + companyId + ", webId=" + webId + "}";
375             }
376 
377             System.out.println(info);
378         }
379     }
380 
381     private static final boolean _PRINT_DUPLICATE_CALLS_TO_GET = false;
382 
383     private static Log _log = LogFactoryUtil.getLog(ConfigurationImpl.class);
384 
385     private ComponentConfiguration _componentConfiguration;
386     private Set<String> _keys = new HashSet<String>();
387 
388 }