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