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