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