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