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.easyconf;
016    
017    import com.germinus.easyconf.AggregatedProperties;
018    import com.germinus.easyconf.ConfigurationException;
019    import com.germinus.easyconf.Conventions;
020    import com.germinus.easyconf.DatasourceURL;
021    import com.germinus.easyconf.FileConfigurationChangedReloadingStrategy;
022    import com.germinus.easyconf.JndiURL;
023    
024    import com.liferay.portal.kernel.log.Log;
025    import com.liferay.portal.kernel.log.LogFactoryUtil;
026    import com.liferay.portal.kernel.util.ArrayUtil;
027    import com.liferay.portal.kernel.util.ReflectionUtil;
028    
029    import java.lang.reflect.Field;
030    
031    import java.net.URL;
032    
033    import java.util.ArrayList;
034    import java.util.List;
035    import java.util.Map;
036    
037    import org.apache.commons.configuration.AbstractFileConfiguration;
038    import org.apache.commons.configuration.CompositeConfiguration;
039    import org.apache.commons.configuration.Configuration;
040    import org.apache.commons.configuration.FileConfiguration;
041    import org.apache.commons.configuration.PropertiesConfiguration;
042    import org.apache.commons.configuration.PropertiesConfigurationLayout;
043    import org.apache.commons.configuration.SubsetConfiguration;
044    import org.apache.commons.configuration.SystemConfiguration;
045    import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
046    
047    /**
048     * @author Raymond Augé
049     */
050    public class ClassLoaderAggregateProperties extends AggregatedProperties {
051    
052            public ClassLoaderAggregateProperties(
053                    ClassLoader classLoader, String companyId, String componentName) {
054    
055                    super(companyId, componentName);
056    
057                    _classLoader = classLoader;
058                    _companyId = companyId;
059                    _componentName = componentName;
060    
061                    _prefixedSystemConfiguration = new SubsetConfiguration(
062                            _systemConfiguration, _getPrefix(), null);
063            }
064    
065            @Override
066            public void addBaseFileName(String fileName) {
067                    URL url = _classLoader.getResource(fileName);
068    
069                    Configuration configuration = _addPropertiesSource(
070                            fileName, url, _baseCompositeConfiguration);
071    
072                    if (configuration != null) {
073                            _baseConfigurationLoaded = true;
074    
075                            if (configuration.isEmpty() && _log.isDebugEnabled()) {
076                                    _log.debug("Empty configuration " + fileName);
077                            }
078                    }
079            }
080    
081            @Override
082            public void addGlobalFileName(String fileName) {
083                    URL url = _classLoader.getResource(fileName);
084    
085                    _addPropertiesSource(fileName, url, _globalCompositeConfiguration);
086            }
087    
088            public CompositeConfiguration getBaseConfiguration() {
089                    return _baseCompositeConfiguration;
090            }
091    
092            @Override
093            public String getComponentName() {
094                    return _componentName;
095            }
096    
097            @Override
098            public Object getProperty(String key) {
099                    Object value = null;
100    
101                    if (value == null) {
102                            value = System.getProperty(_getPrefix().concat(key));
103                    }
104    
105                    if (value == null) {
106                            value = _globalCompositeConfiguration.getProperty(
107                                    _getPrefix().concat(key));
108                    }
109    
110                    if (value == null) {
111                            value = _globalCompositeConfiguration.getProperty(key);
112                    }
113    
114                    if (value == null) {
115                            value = _baseCompositeConfiguration.getProperty(key);
116                    }
117    
118                    if (value == null) {
119                            value = super.getProperty(key);
120                    }
121    
122                    if (value == null) {
123                            value = System.getProperty(key);
124                    }
125    
126                    if ((value == null) && key.equals(Conventions.COMPANY_ID_PROPERTY)) {
127                            value = _companyId;
128                    }
129    
130                    if ((value == null) &&
131                            key.equals(Conventions.COMPONENT_NAME_PROPERTY)) {
132    
133                            value = _componentName;
134                    }
135    
136                    return value;
137            }
138    
139            @Override
140            public boolean hasBaseConfiguration() {
141                    return _baseConfigurationLoaded;
142            }
143    
144            @Override
145            public List<String> loadedSources() {
146                    return _loadedSources;
147            }
148    
149            private Configuration _addDatasourceProperties(String datasourcePath) {
150                    DatasourceURL datasourceURL = new DatasourceURL(
151                            datasourcePath, _companyId, _componentName,
152                            DatasourceURL.PROPERTIES_TABLE);
153    
154                    return datasourceURL.getConfiguration();
155            }
156    
157            private Configuration _addFileProperties(
158                            String fileName,
159                            CompositeConfiguration loadedCompositeConfiguration)
160                    throws ConfigurationException {
161    
162                    try {
163                            FileConfiguration newFileConfiguration =
164                                    new PropertiesConfiguration(fileName);
165    
166                            URL url = newFileConfiguration.getURL();
167    
168                            if (_log.isDebugEnabled()) {
169                                    _log.debug("Adding file " + url);
170                            }
171    
172                            Long delay = _getReloadDelay(
173                                    loadedCompositeConfiguration, newFileConfiguration);
174    
175                            if (delay != null) {
176                                    FileChangedReloadingStrategy fileChangedReloadingStrategy =
177                                            new FileConfigurationChangedReloadingStrategy();
178    
179                                    if (_log.isDebugEnabled()) {
180                                            _log.debug(
181                                                    "File " + url + " will be reloaded every " +
182                                                            delay + " seconds");
183                                    }
184    
185                                    long milliseconds = delay.longValue() * 1000;
186    
187                                    fileChangedReloadingStrategy.setRefreshDelay(milliseconds);
188    
189                                    newFileConfiguration.setReloadingStrategy(
190                                            fileChangedReloadingStrategy);
191                            }
192    
193                            _addIncludedPropertiesSources(
194                                    newFileConfiguration, loadedCompositeConfiguration);
195    
196                            return newFileConfiguration;
197                    }
198                    catch (org.apache.commons.configuration.ConfigurationException ce) {
199                            if (_log.isDebugEnabled()) {
200                                    _log.debug("Configuration source " + fileName + " ignored");
201                            }
202    
203                            return null;
204                    }
205            }
206    
207            private void _addIncludedPropertiesSources(
208                    Configuration newConfiguration,
209                    CompositeConfiguration loadedCompositeConfiguration) {
210    
211                    CompositeConfiguration tempCompositeConfiguration =
212                            new CompositeConfiguration();
213    
214                    tempCompositeConfiguration.addConfiguration(
215                            _prefixedSystemConfiguration);
216                    tempCompositeConfiguration.addConfiguration(newConfiguration);
217                    tempCompositeConfiguration.addConfiguration(_systemConfiguration);
218                    tempCompositeConfiguration.addProperty(
219                            Conventions.COMPANY_ID_PROPERTY, _companyId);
220                    tempCompositeConfiguration.addProperty(
221                            Conventions.COMPONENT_NAME_PROPERTY, _componentName);
222    
223                    String[] fileNames = tempCompositeConfiguration.getStringArray(
224                            Conventions.INCLUDE_PROPERTY);
225    
226                    ArrayUtil.reverse(fileNames);
227    
228                    for (String fileName : fileNames) {
229                            URL url = null;
230    
231                            try {
232                                    url = _classLoader.getResource(fileName);
233                            }
234                            catch (RuntimeException re) {
235                                    if (fileName.startsWith("file:/")) {
236                                            throw re;
237                                    }
238    
239                                    fileName = "file:/".concat(fileName);
240    
241                                    url = _classLoader.getResource(fileName);
242                            }
243    
244                            _addPropertiesSource(fileName, url, loadedCompositeConfiguration);
245                    }
246            }
247    
248            private Configuration _addJNDIProperties(String sourcePath) {
249                    JndiURL jndiURL = new JndiURL(sourcePath, _companyId, _componentName);
250    
251                    return jndiURL.getConfiguration();
252            }
253    
254            private Configuration _addPropertiesSource(
255                    String sourceName, URL url,
256                    CompositeConfiguration loadedCompositeConfiguration) {
257    
258                    try {
259                            Configuration newConfiguration = null;
260    
261                            if (DatasourceURL.isDatasource(sourceName)) {
262                                    newConfiguration = _addDatasourceProperties(sourceName);
263                            }
264                            else if (JndiURL.isJndi(sourceName)) {
265                                    newConfiguration = _addJNDIProperties(sourceName);
266                            }
267                            else if (url != null) {
268                                    newConfiguration = _addURLProperties(
269                                            url, loadedCompositeConfiguration);
270                            }
271                            else {
272                                    newConfiguration = _addFileProperties(
273                                            sourceName, loadedCompositeConfiguration);
274                            }
275    
276                            if (newConfiguration == null) {
277                                    return newConfiguration;
278                            }
279    
280                            loadedCompositeConfiguration.addConfiguration(newConfiguration);
281    
282                            super.addConfiguration(newConfiguration);
283    
284                            if (newConfiguration instanceof AbstractFileConfiguration) {
285                                    AbstractFileConfiguration abstractFileConfiguration =
286                                            (AbstractFileConfiguration)newConfiguration;
287    
288                                    URL abstractFileConfigurationURL =
289                                            abstractFileConfiguration.getURL();
290    
291                                    _loadedSources.add(abstractFileConfigurationURL.toString());
292                            }
293                            else {
294                                    _loadedSources.add(sourceName);
295                            }
296    
297                            return newConfiguration;
298                    }
299                    catch (Exception e) {
300                            if (_log.isDebugEnabled()) {
301                                    _log.debug(
302                                            "Configuration source " + sourceName + " ignored: " +
303                                                    e.getMessage());
304                            }
305    
306                            return null;
307                    }
308            }
309    
310            private Configuration _addURLProperties(
311                            URL url, CompositeConfiguration loadedCompositeConfiguration)
312                    throws ConfigurationException {
313    
314                    try {
315                            PropertiesConfiguration propertiesConfiguration =
316                                    new PropertiesConfiguration(url);
317    
318                            PropertiesConfigurationLayout propertiesConfigurationLayout =
319                                    propertiesConfiguration.getLayout();
320    
321                            try {
322                                    Map<String, Object> layoutData =
323                                            (Map<String, Object>)_layoutDataField.get(
324                                                    propertiesConfigurationLayout);
325    
326                                    for (Object propertyLayoutData : layoutData.values()) {
327                                            _commentField.set(propertyLayoutData, null);
328                                    }
329                            }
330                            catch (ReflectiveOperationException roe) {
331                                    if (_log.isWarnEnabled()) {
332                                            _log.warn(
333                                                    "Unable to clear out comments from " +
334                                                            propertiesConfiguration,
335                                                    roe);
336                                    }
337                            }
338    
339                            if (_log.isDebugEnabled()) {
340                                    _log.debug("Adding resource " + url);
341                            }
342    
343                            Long delay = _getReloadDelay(
344                                    loadedCompositeConfiguration, propertiesConfiguration);
345    
346                            if (delay != null) {
347                                    FileChangedReloadingStrategy fileChangedReloadingStrategy =
348                                            new FileConfigurationChangedReloadingStrategy();
349    
350                                    if (_log.isDebugEnabled()) {
351                                            _log.debug(
352                                                    "Resource " + url + " will be reloaded every " +
353                                                            delay + " seconds");
354                                    }
355    
356                                    long milliseconds = delay.longValue() * 1000;
357    
358                                    fileChangedReloadingStrategy.setRefreshDelay(milliseconds);
359    
360                                    propertiesConfiguration.setReloadingStrategy(
361                                            fileChangedReloadingStrategy);
362                            }
363    
364                            _addIncludedPropertiesSources(
365                                    propertiesConfiguration, loadedCompositeConfiguration);
366    
367                            return propertiesConfiguration;
368                    }
369                    catch (org.apache.commons.configuration.ConfigurationException ce) {
370                            if (_log.isDebugEnabled()) {
371                                    _log.debug("Configuration source " + url + " ignored");
372                            }
373    
374                            return null;
375                    }
376            }
377    
378            private String _getPrefix() {
379                    return _componentName.concat(Conventions.PREFIX_SEPARATOR);
380            }
381    
382            private Long _getReloadDelay(
383                    CompositeConfiguration loadedCompositeConfiguration,
384                    FileConfiguration newFileConfiguration) {
385    
386                    Long delay = newFileConfiguration.getLong(
387                            Conventions.RELOAD_DELAY_PROPERTY, null);
388    
389                    if (delay == null) {
390                            delay = loadedCompositeConfiguration.getLong(
391                                    Conventions.RELOAD_DELAY_PROPERTY, null);
392                    }
393    
394                    return delay;
395            }
396    
397            private static final Log _log = LogFactoryUtil.getLog(
398                    ClassLoaderAggregateProperties.class);
399    
400            private static final Field _commentField;
401            private static final Field _layoutDataField;
402    
403            static {
404                    try {
405                            ClassLoader classLoader =
406                                    PropertiesConfigurationLayout.class.getClassLoader();
407    
408                            Class<?> propertyLayoutDataClass = classLoader.loadClass(
409                                    PropertiesConfigurationLayout.class.getName() +
410                                            "$PropertyLayoutData");
411    
412                            _commentField = ReflectionUtil.getDeclaredField(
413                                    propertyLayoutDataClass, "comment");
414    
415                            _layoutDataField = ReflectionUtil.getDeclaredField(
416                                    PropertiesConfigurationLayout.class, "layoutData");
417                    }
418                    catch (Exception e) {
419                            throw new ExceptionInInitializerError(e);
420                    }
421            }
422    
423            private final CompositeConfiguration _baseCompositeConfiguration =
424                    new CompositeConfiguration();
425            private boolean _baseConfigurationLoaded;
426            private final ClassLoader _classLoader;
427            private final String _companyId;
428            private final String _componentName;
429            private final CompositeConfiguration _globalCompositeConfiguration =
430                    new CompositeConfiguration();
431            private final List<String> _loadedSources = new ArrayList<>();
432            private final Configuration _prefixedSystemConfiguration;
433            private final SystemConfiguration _systemConfiguration =
434                    new SystemConfiguration();
435    
436    }