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