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