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