001    /**
002     * Copyright (c) 2000-2013 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.ComponentConfiguration;
018    import com.germinus.easyconf.ComponentProperties;
019    
020    import com.liferay.portal.configuration.easyconf.ClassLoaderAggregateProperties;
021    import com.liferay.portal.configuration.easyconf.ClassLoaderComponentConfiguration;
022    import com.liferay.portal.kernel.configuration.Filter;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.util.PropertiesUtil;
026    import com.liferay.portal.kernel.util.StringBundler;
027    import com.liferay.portal.kernel.util.Validator;
028    import com.liferay.portal.model.Company;
029    import com.liferay.portal.model.CompanyConstants;
030    import com.liferay.portal.service.CompanyLocalServiceUtil;
031    
032    import java.lang.reflect.Field;
033    
034    import java.util.HashSet;
035    import java.util.Iterator;
036    import java.util.List;
037    import java.util.Map;
038    import java.util.Properties;
039    import java.util.Set;
040    import java.util.concurrent.ConcurrentHashMap;
041    
042    import org.apache.commons.configuration.CompositeConfiguration;
043    import org.apache.commons.configuration.Configuration;
044    import org.apache.commons.configuration.MapConfiguration;
045    
046    /**
047     * @author Brian Wing Shun Chan
048     * @author Shuyang Zhou
049     */
050    public class ConfigurationImpl
051            implements com.liferay.portal.kernel.configuration.Configuration {
052    
053            public ConfigurationImpl(ClassLoader classLoader, String name) {
054                    this(classLoader, name, CompanyConstants.SYSTEM);
055            }
056    
057            public ConfigurationImpl(
058                    ClassLoader classLoader, String name, long companyId) {
059    
060                    String webId = null;
061    
062                    if (companyId > CompanyConstants.SYSTEM) {
063                            try {
064                                    Company company = CompanyLocalServiceUtil.getCompanyById(
065                                            companyId);
066    
067                                    webId = company.getWebId();
068                            }
069                            catch (Exception e) {
070                                    _log.error(e, e);
071                            }
072                    }
073    
074                    _componentConfiguration = new ClassLoaderComponentConfiguration(
075                            classLoader, webId, name);
076    
077                    printSources(companyId, webId);
078            }
079    
080            @Override
081            public void addProperties(Properties properties) {
082                    try {
083                            ComponentProperties componentProperties =
084                                    _componentConfiguration.getProperties();
085    
086                            ClassLoaderAggregateProperties classLoaderAggregateProperties =
087                                    (ClassLoaderAggregateProperties)
088                                            componentProperties.toConfiguration();
089    
090                            Field field1 = CompositeConfiguration.class.getDeclaredField(
091                                    "configList");
092    
093                            field1.setAccessible(true);
094    
095                            // Add to configList of base conf
096    
097                            List<Configuration> configurations =
098                                    (List<Configuration>)field1.get(classLoaderAggregateProperties);
099    
100                            MapConfiguration newConfiguration = new MapConfiguration(
101                                    properties);
102    
103                            configurations.add(0, newConfiguration);
104    
105                            // Add to configList of AggregatedProperties itself
106    
107                            CompositeConfiguration compositeConfiguration =
108                                    classLoaderAggregateProperties.getBaseConfiguration();
109    
110                            configurations = (List<Configuration>)field1.get(
111                                    compositeConfiguration);
112    
113                            configurations.add(0, newConfiguration);
114    
115                            clearCache();
116                    }
117                    catch (Exception e) {
118                            _log.error("The properties could not be added", e);
119                    }
120            }
121    
122            @Override
123            public void clearCache() {
124                    _values.clear();
125            }
126    
127            @Override
128            public boolean contains(String key) {
129                    Object value = _values.get(key);
130    
131                    if (value == null) {
132                            ComponentProperties componentProperties = getComponentProperties();
133    
134                            value = componentProperties.getProperty(key);
135    
136                            if (value == null) {
137                                    value = _nullValue;
138                            }
139    
140                            _values.put(key, value);
141                    }
142    
143                    if (value == _nullValue) {
144                            return false;
145                    }
146    
147                    return true;
148            }
149    
150            @Override
151            public String get(String key) {
152                    Object value = _values.get(key);
153    
154                    if (value == null) {
155                            ComponentProperties componentProperties = getComponentProperties();
156    
157                            value = componentProperties.getString(key);
158    
159                            if (value == null) {
160                                    value = _nullValue;
161                            }
162    
163                            _values.put(key, value);
164                    }
165                    else if (_PRINT_DUPLICATE_CALLS_TO_GET) {
166                            System.out.println("Duplicate call to get " + key);
167                    }
168    
169                    if (value instanceof String) {
170                            return (String)value;
171                    }
172    
173                    return null;
174            }
175    
176            @Override
177            public String get(String key, Filter filter) {
178                    String filterCacheKey = buildFilterCacheKey(key, filter, false);
179    
180                    Object value = null;
181    
182                    if (filterCacheKey != null) {
183                            value = _values.get(filterCacheKey);
184                    }
185    
186                    if (value == null) {
187                            ComponentProperties componentProperties = getComponentProperties();
188    
189                            value = componentProperties.getString(
190                                    key, getEasyConfFilter(filter));
191    
192                            if (filterCacheKey != null) {
193                                    if (value == null) {
194                                            value = _nullValue;
195                                    }
196    
197                                    _values.put(filterCacheKey, value);
198                            }
199                    }
200    
201                    if (value instanceof String) {
202                            return (String)value;
203                    }
204    
205                    return null;
206            }
207    
208            @Override
209            public String[] getArray(String key) {
210                    String cacheKey = _ARRAY_KEY_PREFIX.concat(key);
211    
212                    Object value = _values.get(cacheKey);
213    
214                    if (value == null) {
215                            ComponentProperties componentProperties = getComponentProperties();
216    
217                            String[] array = componentProperties.getStringArray(key);
218    
219                            value = fixArrayValue(cacheKey, array);
220                    }
221    
222                    if (value instanceof String[]) {
223                            return (String[])value;
224                    }
225    
226                    return _emptyArray;
227            }
228    
229            @Override
230            public String[] getArray(String key, Filter filter) {
231                    String filterCacheKey = buildFilterCacheKey(key, filter, true);
232    
233                    Object value = null;
234    
235                    if (filterCacheKey != null) {
236                            value = _values.get(filterCacheKey);
237                    }
238    
239                    if (value == null) {
240                            ComponentProperties componentProperties = getComponentProperties();
241    
242                            String[] array = componentProperties.getStringArray(
243                                    key, getEasyConfFilter(filter));
244    
245                            value = fixArrayValue(filterCacheKey, array);
246                    }
247    
248                    if (value instanceof String[]) {
249                            return (String[])value;
250                    }
251    
252                    return _emptyArray;
253            }
254    
255            @Override
256            public Properties getProperties() {
257    
258                    // For some strange reason, componentProperties.getProperties() returns
259                    // values with spaces after commas. So a property setting of "xyz=1,2,3"
260                    // actually returns "xyz=1, 2, 3". This can break applications that
261                    // don't expect that extra space. However, getting the property value
262                    // directly through componentProperties returns the correct value. This
263                    // method fixes the weird behavior by returning properties with the
264                    // correct values.
265    
266                    Properties properties = new Properties();
267    
268                    ComponentProperties componentProperties = getComponentProperties();
269    
270                    Properties componentPropertiesProperties =
271                            componentProperties.getProperties();
272    
273                    for (Map.Entry<Object, Object> entry :
274                                    componentPropertiesProperties.entrySet()) {
275    
276                            String key = (String)entry.getKey();
277                            String value = (String)entry.getValue();
278    
279                            properties.setProperty(key, value);
280                    }
281    
282                    return properties;
283            }
284    
285            @Override
286            public Properties getProperties(String prefix, boolean removePrefix) {
287                    Properties properties = getProperties();
288    
289                    return PropertiesUtil.getProperties(properties, prefix, removePrefix);
290            }
291    
292            @Override
293            public void removeProperties(Properties properties) {
294                    try {
295                            ComponentProperties componentProperties =
296                                    _componentConfiguration.getProperties();
297    
298                            ClassLoaderAggregateProperties classLoaderAggregateProperties =
299                                    (ClassLoaderAggregateProperties)
300                                            componentProperties.toConfiguration();
301    
302                            CompositeConfiguration compositeConfiguration =
303                                    classLoaderAggregateProperties.getBaseConfiguration();
304    
305                            Field field2 = CompositeConfiguration.class.getDeclaredField(
306                                    "configList");
307    
308                            field2.setAccessible(true);
309    
310                            @SuppressWarnings("unchecked")
311                            List<Configuration> configurations =
312                                    (List<Configuration>)field2.get(compositeConfiguration);
313    
314                            Iterator<Configuration> itr = configurations.iterator();
315    
316                            while (itr.hasNext()) {
317                                    Configuration configuration = itr.next();
318    
319                                    if (!(configuration instanceof MapConfiguration)) {
320                                            break;
321                                    }
322    
323                                    MapConfiguration mapConfiguration =
324                                            (MapConfiguration)configuration;
325    
326                                    if (mapConfiguration.getMap() == properties) {
327                                            itr.remove();
328    
329                                            classLoaderAggregateProperties.removeConfiguration(
330                                                    configuration);
331                                    }
332                            }
333    
334                            clearCache();
335                    }
336                    catch (Exception e) {
337                            _log.error("The properties could not be removed", e);
338                    }
339            }
340    
341            @Override
342            public void set(String key, String value) {
343                    ComponentProperties componentProperties = getComponentProperties();
344    
345                    componentProperties.setProperty(key, value);
346    
347                    _values.put(key, value);
348            }
349    
350            protected String buildFilterCacheKey(
351                    String key, Filter filter, boolean arrayValue) {
352    
353                    if (filter.getVariables() != null) {
354                            return null;
355                    }
356    
357                    String[] selectors = filter.getSelectors();
358    
359                    int length = 0;
360    
361                    if (arrayValue) {
362                            length = selectors.length + 2;
363                    }
364                    else {
365                            length = selectors.length + 1;
366                    }
367    
368                    StringBundler sb = new StringBundler(length);
369    
370                    if (arrayValue) {
371                            sb.append(_ARRAY_KEY_PREFIX);
372                    }
373    
374                    sb.append(key);
375                    sb.append(selectors);
376    
377                    return sb.toString();
378            }
379    
380            protected Object fixArrayValue(String cacheKey, String[] array) {
381                    if (cacheKey == null) {
382                            return array;
383                    }
384    
385                    Object value = _nullValue;
386    
387                    if ((array != null) && (array.length > 0)) {
388    
389                            // Commons Configuration parses an empty property into a String
390                            // array with one String containing one space. It also leaves a
391                            // trailing array member if you set a property in more than one
392                            // line.
393    
394                            if (Validator.isNull(array[array.length - 1])) {
395                                    String[] subArray = new String[array.length - 1];
396    
397                                    System.arraycopy(array, 0, subArray, 0, subArray.length);
398    
399                                    array = subArray;
400                            }
401    
402                            if (array.length > 0) {
403                                    value = array;
404                            }
405                    }
406    
407                    _values.put(cacheKey, value);
408    
409                    return value;
410            }
411    
412            protected ComponentProperties getComponentProperties() {
413                    return _componentConfiguration.getProperties();
414            }
415    
416            protected com.germinus.easyconf.Filter getEasyConfFilter(Filter filter) {
417                    com.germinus.easyconf.Filter easyConfFilter =
418                            com.germinus.easyconf.Filter.by(filter.getSelectors());
419    
420                    if (filter.getVariables() != null) {
421                            easyConfFilter.setVariables(filter.getVariables());
422                    }
423    
424                    return easyConfFilter;
425            }
426    
427            protected void printSources(long companyId, String webId) {
428                    ComponentProperties componentProperties = getComponentProperties();
429    
430                    List<String> sources = componentProperties.getLoadedSources();
431    
432                    for (int i = sources.size() - 1; i >= 0; i--) {
433                            String source = sources.get(i);
434    
435                            if (_printedSources.contains(source)) {
436                                    continue;
437                            }
438    
439                            _printedSources.add(source);
440    
441                            String info = "Loading " + source;
442    
443                            if (companyId > CompanyConstants.SYSTEM) {
444                                    info +=
445                                            " for {companyId=" + companyId + ", webId=" + webId + "}";
446                            }
447    
448                            System.out.println(info);
449                    }
450            }
451    
452            private static final String _ARRAY_KEY_PREFIX = "ARRAY_";
453    
454            private static final boolean _PRINT_DUPLICATE_CALLS_TO_GET = false;
455    
456            private static Log _log = LogFactoryUtil.getLog(ConfigurationImpl.class);
457    
458            private static String[] _emptyArray = new String[0];
459            private static Object _nullValue = new Object();
460    
461            private ComponentConfiguration _componentConfiguration;
462            private Set<String> _printedSources = new HashSet<String>();
463            private Map<String, Object> _values =
464                    new ConcurrentHashMap<String, Object>();
465    
466    }