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