001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.configuration;
016    
017    import com.germinus.easyconf.AggregatedProperties;
018    import com.germinus.easyconf.ComponentConfiguration;
019    import com.germinus.easyconf.ComponentProperties;
020    import com.germinus.easyconf.Conventions;
021    import com.germinus.easyconf.EasyConf;
022    
023    import com.liferay.portal.kernel.configuration.Filter;
024    import com.liferay.portal.kernel.log.Log;
025    import com.liferay.portal.kernel.log.LogFactoryUtil;
026    import com.liferay.portal.kernel.util.PropertiesUtil;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.kernel.util.StringUtil;
029    import com.liferay.portal.kernel.util.Validator;
030    import com.liferay.portal.model.Company;
031    import com.liferay.portal.model.CompanyConstants;
032    import com.liferay.portal.service.CompanyLocalServiceUtil;
033    
034    import java.io.FileWriter;
035    import java.io.Writer;
036    
037    import java.lang.reflect.Field;
038    
039    import java.net.URI;
040    import java.net.URISyntaxException;
041    import java.net.URL;
042    
043    import java.util.HashSet;
044    import java.util.Iterator;
045    import java.util.List;
046    import java.util.Map;
047    import java.util.Properties;
048    import java.util.Set;
049    
050    import org.apache.commons.configuration.CompositeConfiguration;
051    import org.apache.commons.configuration.Configuration;
052    import org.apache.commons.configuration.MapConfiguration;
053    
054    /**
055     * @author Brian Wing Shun Chan
056     */
057    public class ConfigurationImpl
058            implements com.liferay.portal.kernel.configuration.Configuration {
059    
060            public ConfigurationImpl(ClassLoader classLoader, String name) {
061                    this(classLoader, name, CompanyConstants.SYSTEM);
062            }
063    
064            public ConfigurationImpl(
065                    ClassLoader classLoader, String name, long companyId) {
066    
067                    try {
068                            URL url = classLoader.getResource(
069                                    name + Conventions.PROPERTIES_EXTENSION);
070    
071                            if ((url != null) && url.getProtocol().equals("file")) {
072                                    String basePath = url.getPath();
073    
074                                    int pos = name.lastIndexOf(
075                                            StringPool.SLASH + name + Conventions.PROPERTIES_EXTENSION);
076    
077                                    if (pos != -1) {
078                                            basePath = basePath.substring(0, pos);
079                                    }
080    
081                                    Properties properties = new Properties();
082    
083                                    properties.load(url.openStream());
084    
085                                    if (!properties.containsKey("base.path")) {
086                                            String fileName = StringUtil.replace(
087                                                    url.getFile(), "%20", StringPool.SPACE);
088    
089                                            Writer writer = new FileWriter(fileName, true);
090    
091                                            writer.write("\n\nbase.path=".concat(basePath));
092    
093                                            writer.close();
094                                    }
095                            }
096                    }
097                    catch (Exception e) {
098                            _log.error(e, e);
099                    }
100    
101                    String webId = null;
102    
103                    if (companyId > CompanyConstants.SYSTEM) {
104                            try {
105                                    Company company = CompanyLocalServiceUtil.getCompanyById(
106                                            companyId);
107    
108                                    webId = company.getWebId();
109                            }
110                            catch (Exception e) {
111                                    _log.error(e, e);
112                            }
113                    }
114    
115                    if (webId != null) {
116                            _componentConfiguration = EasyConf.getConfiguration(
117                                    webId, getFileName(classLoader, name));
118                    }
119                    else {
120                            _componentConfiguration = EasyConf.getConfiguration(
121                                    getFileName(classLoader, name));
122                    }
123    
124                    printSources(companyId, webId);
125            }
126    
127            public void addProperties(Properties properties) {
128                    try {
129                            ComponentProperties componentProperties =
130                                    _componentConfiguration.getProperties();
131    
132                            AggregatedProperties aggregatedProperties =
133                                    (AggregatedProperties)componentProperties.toConfiguration();
134    
135                            Field field1 = CompositeConfiguration.class.getDeclaredField(
136                                    "configList");
137    
138                            field1.setAccessible(true);
139    
140                            // Add to configList of base conf
141    
142                            List<Configuration> configurations =
143                                    (List<Configuration>)field1.get(aggregatedProperties);
144    
145                            MapConfiguration newConfiguration =
146                                    new MapConfiguration(properties);
147    
148                            configurations.add(0, newConfiguration);
149    
150                            // Add to configList of AggregatedProperties itself
151    
152                            Field field2 = aggregatedProperties.getClass().getDeclaredField(
153                                    "baseConf");
154    
155                            field2.setAccessible(true);
156    
157                            CompositeConfiguration compositeConfiguration =
158                                    (CompositeConfiguration)field2.get(aggregatedProperties);
159    
160                            configurations = (List<Configuration>)field1.get(
161                                    compositeConfiguration);
162    
163                            configurations.add(0, newConfiguration);
164                    }
165                    catch (Exception e) {
166                            _log.error("The properties could not be added", e);
167                    }
168            }
169    
170            public boolean contains(String key) {
171                    return getComponentProperties().containsKey(key);
172            }
173    
174            public String get(String key) {
175                    if (_PRINT_DUPLICATE_CALLS_TO_GET) {
176                            if (_keys.contains(key)) {
177                                    System.out.println("Duplicate call to get " + key);
178                            }
179                            else {
180                                    _keys.add(key);
181                            }
182                    }
183    
184                    return getComponentProperties().getString(key);
185            }
186    
187            public String get(String key, Filter filter) {
188                    return getComponentProperties().getString(
189                            key, getEasyConfFilter(filter));
190            }
191    
192            public String[] getArray(String key) {
193                    String[] array = getComponentProperties().getStringArray(key);
194    
195                    if (array == null) {
196                            return new String[0];
197                    }
198                    else if (array.length > 0) {
199    
200                            // Commons Configuration parses an empty property into a String
201                            // array with one String containing one space. It also leaves a
202                            // trailing array member if you set a property in more than one
203                            // line.
204    
205                            if (Validator.isNull(array[array.length - 1])) {
206                                    String[] subArray = new String[array.length - 1];
207    
208                                    System.arraycopy(array, 0, subArray, 0, subArray.length);
209    
210                                    array = subArray;
211                            }
212                    }
213    
214                    return array;
215            }
216    
217            public String[] getArray(String key, Filter filter) {
218                    return getComponentProperties().getStringArray(
219                            key, getEasyConfFilter(filter));
220            }
221    
222            public Properties getProperties() {
223    
224                    // For some strange reason, componentProperties.getProperties() returns
225                    // values with spaces after commas. So a property setting of "xyz=1,2,3"
226                    // actually returns "xyz=1, 2, 3". This can break applications that
227                    // don't expect that extra space. However, getting the property value
228                    // directly through componentProperties returns the correct value. This
229                    // method fixes the weird behavior by returing properties with the
230                    // correct values.
231    
232                    Properties properties = new Properties();
233    
234                    ComponentProperties componentProperties = getComponentProperties();
235    
236                    Iterator<Map.Entry<Object, Object>> itr =
237                            componentProperties.getProperties().entrySet().iterator();
238    
239                    while (itr.hasNext()) {
240                            Map.Entry<Object, Object> entry = itr.next();
241    
242                            String key = (String)entry.getKey();
243                            String value = (String)entry.getValue();
244    
245                            properties.setProperty(key, value);
246                    }
247    
248                    return properties;
249            }
250    
251            public Properties getProperties(String prefix, boolean removePrefix) {
252                    Properties allProperties = getProperties();
253    
254                    return PropertiesUtil.getProperties(
255                            allProperties, prefix, removePrefix);
256            }
257    
258            public void removeProperties(Properties properties) {
259                    try {
260                            ComponentProperties componentProperties =
261                                    _componentConfiguration.getProperties();
262    
263                            AggregatedProperties aggregatedProperties =
264                                    (AggregatedProperties)componentProperties.toConfiguration();
265    
266                            Field field1 = aggregatedProperties.getClass().getDeclaredField(
267                                    "baseConf");
268    
269                            field1.setAccessible(true);
270    
271                            CompositeConfiguration compositeConfiguration =
272                                    (CompositeConfiguration)field1.get(aggregatedProperties);
273    
274                            Field field2 = CompositeConfiguration.class.getDeclaredField(
275                                    "configList");
276    
277                            field2.setAccessible(true);
278    
279                            List<Configuration> configurations =
280                                    (List<Configuration>)field2.get(compositeConfiguration);
281    
282                            Iterator<Configuration> itr = configurations.iterator();
283    
284                            while (itr.hasNext()) {
285                                    Configuration configuration = itr.next();
286    
287                                    if (!(configuration instanceof MapConfiguration)) {
288                                            return;
289                                    }
290    
291                                    MapConfiguration mapConfiguration =
292                                            (MapConfiguration)configuration;
293    
294                                    if (mapConfiguration.getMap() == properties) {
295                                            itr.remove();
296    
297                                            aggregatedProperties.removeConfiguration(configuration);
298                                    }
299                            }
300                    }
301                    catch (Exception e) {
302                            _log.error("The properties could not be removed", e);
303                    }
304            }
305    
306            public void set(String key, String value) {
307                    getComponentProperties().setProperty(key, value);
308            }
309    
310            protected ComponentProperties getComponentProperties() {
311                    return _componentConfiguration.getProperties();
312            }
313    
314            protected com.germinus.easyconf.Filter getEasyConfFilter(Filter filter) {
315                    com.germinus.easyconf.Filter easyConfFilter =
316                            com.germinus.easyconf.Filter.by(filter.getSelectors());
317    
318                    if (filter.getVariables() != null) {
319                            easyConfFilter.setVariables(filter.getVariables());
320                    }
321    
322                    return easyConfFilter;
323            }
324    
325            protected String getFileName(ClassLoader classLoader, String name) {
326                    URL url = classLoader.getResource(name + ".properties");
327    
328                    // If the resource is located inside of a JAR, then EasyConf needs the
329                    // "jar:file:" prefix appended to the path. Use URL.toExternalForm() to
330                    // achieve that. When running under JBoss, the protocol returned is
331                    // "vfsfile" or "vfszip". When running under OC4J, the protocol returned
332                    // is "code-source". When running under WebLogic, the protocol returned
333                    // is "zip". When running under WebSphere, the protocol returned is
334                    // "wsjar".
335    
336                    String protocol = url.getProtocol();
337    
338                    if (protocol.equals("code-source") || protocol.equals("jar") ||
339                            protocol.equals("vfsfile") || protocol.equals("vfszip") ||
340                            protocol.equals("wsjar") || protocol.equals("zip")) {
341    
342                            name = url.toExternalForm();
343                    }
344                    else {
345                            try {
346                                    name = new URI(url.getPath()).getPath();
347                            }
348                            catch (URISyntaxException urise) {
349                                    name = url.getFile();
350                            }
351                    }
352    
353                    int pos = name.lastIndexOf(".properties");
354    
355                    if (pos != -1) {
356                            name = name.substring(0, pos);
357                    }
358    
359                    return name;
360            }
361    
362            protected void printSources(long companyId, String webId) {
363                    List<String> sources = getComponentProperties().getLoadedSources();
364    
365                    for (int i = sources.size() - 1; i >= 0; i--) {
366                            String source = sources.get(i);
367    
368                            if (_printedSources.contains(source)) {
369                                    continue;
370                            }
371    
372                            _printedSources.add(source);
373    
374                            String info = "Loading " + source;
375    
376                            if (companyId > CompanyConstants.SYSTEM) {
377                                    info +=
378                                            " for {companyId=" + companyId + ", webId=" + webId + "}";
379                            }
380    
381                            System.out.println(info);
382                    }
383            }
384    
385            private static final boolean _PRINT_DUPLICATE_CALLS_TO_GET = false;
386    
387            private static Log _log = LogFactoryUtil.getLog(ConfigurationImpl.class);
388    
389            private ComponentConfiguration _componentConfiguration;
390            private Set<String> _keys = new HashSet<String>();
391            private Set<String> _printedSources = new HashSet<String>();
392    
393    }