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