001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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            public ConfigurationImpl(
082                    ClassLoader classLoader, String name, long companyId, String webId) {
083    
084                    _componentConfiguration = new ClassLoaderComponentConfiguration(
085                            classLoader, webId, name);
086    
087                    printSources(companyId, webId);
088            }
089    
090            @Override
091            public void addProperties(Properties properties) {
092                    try {
093                            ComponentProperties componentProperties =
094                                    _componentConfiguration.getProperties();
095    
096                            ClassLoaderAggregateProperties classLoaderAggregateProperties =
097                                    (ClassLoaderAggregateProperties)
098                                            componentProperties.toConfiguration();
099    
100                            Field field1 = CompositeConfiguration.class.getDeclaredField(
101                                    "configList");
102    
103                            field1.setAccessible(true);
104    
105                            // Add to configList of base conf
106    
107                            List<Configuration> configurations =
108                                    (List<Configuration>)field1.get(classLoaderAggregateProperties);
109    
110                            MapConfiguration newConfiguration = new MapConfiguration(
111                                    properties);
112    
113                            configurations.add(0, newConfiguration);
114    
115                            // Add to configList of AggregatedProperties itself
116    
117                            CompositeConfiguration compositeConfiguration =
118                                    classLoaderAggregateProperties.getBaseConfiguration();
119    
120                            configurations = (List<Configuration>)field1.get(
121                                    compositeConfiguration);
122    
123                            configurations.add(0, newConfiguration);
124    
125                            clearCache();
126                    }
127                    catch (Exception e) {
128                            _log.error("The properties could not be added", e);
129                    }
130            }
131    
132            @Override
133            public void clearCache() {
134                    _values.clear();
135            }
136    
137            @Override
138            public boolean contains(String key) {
139                    Object value = _values.get(key);
140    
141                    if (value == null) {
142                            ComponentProperties componentProperties = getComponentProperties();
143    
144                            value = componentProperties.getProperty(key);
145    
146                            if (value == null) {
147                                    value = _nullValue;
148                            }
149    
150                            _values.put(key, value);
151                    }
152    
153                    if (value == _nullValue) {
154                            return false;
155                    }
156    
157                    return true;
158            }
159    
160            @Override
161            public String get(String key) {
162                    Object value = _values.get(key);
163    
164                    if (value == null) {
165                            ComponentProperties componentProperties = getComponentProperties();
166    
167                            value = componentProperties.getString(key);
168    
169                            if (value == null) {
170                                    value = _nullValue;
171                            }
172    
173                            _values.put(key, value);
174                    }
175                    else if (_PRINT_DUPLICATE_CALLS_TO_GET) {
176                            System.out.println("Duplicate call to get " + key);
177                    }
178    
179                    if (value instanceof String) {
180                            return (String)value;
181                    }
182    
183                    return null;
184            }
185    
186            @Override
187            public String get(String key, Filter filter) {
188                    String filterCacheKey = buildFilterCacheKey(key, filter, false);
189    
190                    Object value = null;
191    
192                    if (filterCacheKey != null) {
193                            value = _values.get(filterCacheKey);
194                    }
195    
196                    if (value == null) {
197                            ComponentProperties componentProperties = getComponentProperties();
198    
199                            value = componentProperties.getString(
200                                    key, getEasyConfFilter(filter));
201    
202                            if (filterCacheKey != null) {
203                                    if (value == null) {
204                                            value = _nullValue;
205                                    }
206    
207                                    _values.put(filterCacheKey, value);
208                            }
209                    }
210    
211                    if (value instanceof String) {
212                            return (String)value;
213                    }
214    
215                    return null;
216            }
217    
218            @Override
219            public String[] getArray(String key) {
220                    String cacheKey = _ARRAY_KEY_PREFIX.concat(key);
221    
222                    Object value = _values.get(cacheKey);
223    
224                    if (value == null) {
225                            ComponentProperties componentProperties = getComponentProperties();
226    
227                            String[] array = componentProperties.getStringArray(key);
228    
229                            value = fixArrayValue(cacheKey, array);
230                    }
231    
232                    if (value instanceof String[]) {
233                            return (String[])value;
234                    }
235    
236                    return _emptyArray;
237            }
238    
239            @Override
240            public String[] getArray(String key, Filter filter) {
241                    String filterCacheKey = buildFilterCacheKey(key, filter, true);
242    
243                    Object value = null;
244    
245                    if (filterCacheKey != null) {
246                            value = _values.get(filterCacheKey);
247                    }
248    
249                    if (value == null) {
250                            ComponentProperties componentProperties = getComponentProperties();
251    
252                            String[] array = componentProperties.getStringArray(
253                                    key, getEasyConfFilter(filter));
254    
255                            value = fixArrayValue(filterCacheKey, array);
256                    }
257    
258                    if (value instanceof String[]) {
259                            return (String[])value;
260                    }
261    
262                    return _emptyArray;
263            }
264    
265            @Override
266            public Properties getProperties() {
267    
268                    // For some strange reason, componentProperties.getProperties() returns
269                    // values with spaces after commas. So a property setting of "xyz=1,2,3"
270                    // actually returns "xyz=1, 2, 3". This can break applications that
271                    // don't expect that extra space. However, getting the property value
272                    // directly through componentProperties returns the correct value. This
273                    // method fixes the weird behavior by returning properties with the
274                    // correct values.
275    
276                    Properties properties = new Properties();
277    
278                    ComponentProperties componentProperties = getComponentProperties();
279    
280                    Properties componentPropertiesProperties =
281                            componentProperties.getProperties();
282    
283                    for (String key : componentPropertiesProperties.stringPropertyNames()) {
284                            properties.setProperty(key, componentProperties.getString(key));
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 =
301                                    _componentConfiguration.getProperties();
302    
303                            ClassLoaderAggregateProperties classLoaderAggregateProperties =
304                                    (ClassLoaderAggregateProperties)
305                                            componentProperties.toConfiguration();
306    
307                            CompositeConfiguration compositeConfiguration =
308                                    classLoaderAggregateProperties.getBaseConfiguration();
309    
310                            Field field2 = CompositeConfiguration.class.getDeclaredField(
311                                    "configList");
312    
313                            field2.setAccessible(true);
314    
315                            @SuppressWarnings("unchecked")
316                            List<Configuration> configurations =
317                                    (List<Configuration>)field2.get(compositeConfiguration);
318    
319                            Iterator<Configuration> itr = configurations.iterator();
320    
321                            while (itr.hasNext()) {
322                                    Configuration configuration = itr.next();
323    
324                                    if (!(configuration instanceof MapConfiguration)) {
325                                            break;
326                                    }
327    
328                                    MapConfiguration mapConfiguration =
329                                            (MapConfiguration)configuration;
330    
331                                    if (mapConfiguration.getMap() == properties) {
332                                            itr.remove();
333    
334                                            classLoaderAggregateProperties.removeConfiguration(
335                                                    configuration);
336                                    }
337                            }
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 Log _log = LogFactoryUtil.getLog(ConfigurationImpl.class);
462    
463            private static String[] _emptyArray = new String[0];
464            private static Object _nullValue = new Object();
465    
466            private ComponentConfiguration _componentConfiguration;
467            private Set<String> _printedSources = new HashSet<String>();
468            private Map<String, Object> _values =
469                    new ConcurrentHashMap<String, Object>();
470    
471    }