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.BufferedWriter;
43  import java.io.FileWriter;
44  import java.io.Writer;
45  
46  import java.lang.reflect.Field;
47  
48  import java.net.URI;
49  import java.net.URISyntaxException;
50  import java.net.URL;
51  
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 allProperties = getProperties();
265 
266         return PropertiesUtil.getProperties(
267             allProperties, prefix, removePrefix);
268     }
269 
270     public void removeProperties(Properties properties) {
271         try {
272             ComponentProperties componentProperties =
273                 _componentConfiguration.getProperties();
274 
275             AggregatedProperties aggregatedProperties =
276                 (AggregatedProperties)componentProperties.toConfiguration();
277 
278             Field field1 = aggregatedProperties.getClass().getDeclaredField(
279                 "baseConf");
280 
281             field1.setAccessible(true);
282 
283             CompositeConfiguration compositeConfiguration =
284                 (CompositeConfiguration)field1.get(aggregatedProperties);
285 
286             Field field2 = CompositeConfiguration.class.getDeclaredField(
287                 "configList");
288 
289             field2.setAccessible(true);
290 
291             List<Configuration> configurations =
292                 (List<Configuration>)field2.get(compositeConfiguration);
293 
294             Iterator<Configuration> itr = configurations.iterator();
295 
296             while (itr.hasNext()) {
297                 Configuration configuration = itr.next();
298 
299                 if (!(configuration instanceof MapConfiguration)) {
300                     return;
301                 }
302 
303                 MapConfiguration mapConfiguration =
304                     (MapConfiguration)configuration;
305 
306                 if (mapConfiguration.getMap() == properties) {
307                     itr.remove();
308 
309                     aggregatedProperties.removeConfiguration(configuration);
310                 }
311             }
312         }
313         catch (Exception e) {
314             _log.error("The properties could not be removed", e);
315         }
316     }
317 
318     public void set(String key, String value) {
319         getComponentProperties().setProperty(key, value);
320     }
321 
322     protected ComponentProperties getComponentProperties() {
323         return _componentConfiguration.getProperties();
324     }
325 
326     protected com.germinus.easyconf.Filter getEasyConfFilter(Filter filter) {
327         com.germinus.easyconf.Filter easyConfFilter =
328             com.germinus.easyconf.Filter.by(filter.getSelectors());
329 
330         if (filter.getVariables() != null) {
331             easyConfFilter.setVariables(filter.getVariables());
332         }
333 
334         return easyConfFilter;
335     }
336 
337     protected String getFileName(ClassLoader classLoader, String name) {
338         URL url = classLoader.getResource(name + ".properties");
339 
340         // If the resource is located inside of a JAR, then EasyConf needs the
341         // "jar:file:" prefix appended to the path. Use URL.toExternalForm() to
342         // achieve that. When running under JBoss, the protocol returned is
343         // "vfszip". When running under OC4J, the protocol returned is
344         // "code-source". When running under WebLogic, the protocol returned is
345         // "zip". When running under WebSphere, the protocol returned is
346         // "wsjar".
347 
348         String protocol = url.getProtocol();
349 
350         if (protocol.equals("code-source") || protocol.equals("jar") ||
351             protocol.equals("vfszip") || protocol.equals("wsjar") ||
352             protocol.equals("zip")) {
353 
354             name = url.toExternalForm();
355         }
356         else {
357             try {
358                 name = new URI(url.getPath()).getPath();
359             }
360             catch (URISyntaxException urise) {
361                 name = url.getFile();
362             }
363         }
364 
365         int pos = name.lastIndexOf(".properties");
366 
367         if (pos != -1) {
368             name = name.substring(0, pos);
369         }
370 
371         return name;
372     }
373 
374     protected void printSources(long companyId, String webId) {
375         List<String> sources = getComponentProperties().getLoadedSources();
376 
377         for (int i = sources.size() - 1; i >= 0; i--) {
378             String source = sources.get(i);
379 
380             String info = "Loading " + source;
381 
382             if (companyId > CompanyConstants.SYSTEM) {
383                 info +=
384                     " for {companyId=" + companyId + ", webId=" + webId + "}";
385             }
386 
387             System.out.println(info);
388         }
389     }
390 
391     private static final boolean _PRINT_DUPLICATE_CALLS_TO_GET = false;
392 
393     private static Log _log = LogFactoryUtil.getLog(ConfigurationImpl.class);
394 
395     private ComponentConfiguration _componentConfiguration;
396     private Set<String> _keys = new HashSet<String>();
397 
398 }