/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.configuration;
import com.germinus.easyconf.ComponentConfiguration;
import com.germinus.easyconf.ComponentProperties;
import com.liferay.portal.configuration.easyconf.ClassLoaderAggregateProperties;
import com.liferay.portal.configuration.easyconf.ClassLoaderComponentConfiguration;
import com.liferay.portal.kernel.configuration.Filter;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.Company;
import com.liferay.portal.kernel.model.CompanyConstants;
import com.liferay.portal.kernel.service.CompanyLocalServiceUtil;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.HashUtil;
import com.liferay.portal.kernel.util.PropertiesUtil;
import com.liferay.portal.kernel.util.Validator;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.MapConfiguration;
/**
* @author Brian Wing Shun Chan
* @author Shuyang Zhou
*/
public class ConfigurationImpl
implements com.liferay.portal.kernel.configuration.Configuration {
/**
* @deprecated As of 7.0.0, replaced by {@link
* #ConfigurationImpl(ClassLoader, String, long, String)}
*/
@Deprecated
public ConfigurationImpl(ClassLoader classLoader, String name) {
this(classLoader, name, CompanyConstants.SYSTEM);
}
/**
* @deprecated As of 7.0.0, replaced by {@link
* #ConfigurationImpl(ClassLoader, String, long, String)}
*/
@Deprecated
public ConfigurationImpl(
ClassLoader classLoader, String name, long companyId) {
String webId = null;
if (companyId > CompanyConstants.SYSTEM) {
try {
Company company = CompanyLocalServiceUtil.getCompanyById(
companyId);
webId = company.getWebId();
}
catch (Exception e) {
_log.error(e, e);
}
}
_componentConfiguration = new ClassLoaderComponentConfiguration(
classLoader, webId, name);
printSources(companyId, webId);
}
public ConfigurationImpl(
ClassLoader classLoader, String name, long companyId, String webId) {
_componentConfiguration = new ClassLoaderComponentConfiguration(
classLoader, webId, name);
printSources(companyId, webId);
}
@Override
public void addProperties(Properties properties) {
try {
ComponentProperties componentProperties = getComponentProperties();
ClassLoaderAggregateProperties classLoaderAggregateProperties =
(ClassLoaderAggregateProperties)
componentProperties.toConfiguration();
Field field1 = CompositeConfiguration.class.getDeclaredField(
"configList");
field1.setAccessible(true);
// Add to configList of base conf
List<Configuration> configurations =
(List<Configuration>)field1.get(classLoaderAggregateProperties);
MapConfiguration newConfiguration = new MapConfiguration(
_castPropertiesToMap(properties));
newConfiguration.setTrimmingDisabled(true);
configurations.add(0, newConfiguration);
// Add to configList of AggregatedProperties itself
CompositeConfiguration compositeConfiguration =
classLoaderAggregateProperties.getBaseConfiguration();
configurations = (List<Configuration>)field1.get(
compositeConfiguration);
configurations.add(0, newConfiguration);
_properties = null;
clearCache();
}
catch (Exception e) {
_log.error("The properties could not be added", e);
}
}
@Override
public void clearCache() {
_configurationArrayCache.clear();
_configurationCache.clear();
_configurationFilterArrayCache.clear();
_configurationFilterCache.clear();
_properties = null;
}
@Override
public boolean contains(String key) {
Object value = _configurationCache.get(key);
if (value == null) {
ComponentProperties componentProperties = getComponentProperties();
value = componentProperties.getProperty(key);
if (value == null) {
value = _nullValue;
}
_configurationCache.put(key, value);
}
if (value == _nullValue) {
return false;
}
return true;
}
@Override
public String get(String key) {
Object value = _configurationCache.get(key);
if (value == null) {
ComponentProperties componentProperties = getComponentProperties();
value = componentProperties.getString(key);
if (value == null) {
value = _nullValue;
}
_configurationCache.put(key, value);
}
else if (_PRINT_DUPLICATE_CALLS_TO_GET) {
System.out.println("Duplicate call to get " + key);
}
if (value instanceof String) {
return (String)value;
}
return null;
}
@Override
public String get(String key, Filter filter) {
FilterCacheKey filterCacheKey = _buildFilterCacheKey(key, filter);
Object value = null;
if (filterCacheKey != null) {
value = _configurationFilterCache.get(filterCacheKey);
}
if (value == null) {
ComponentProperties componentProperties = getComponentProperties();
value = componentProperties.getString(
key, getEasyConfFilter(filter));
if (filterCacheKey != null) {
if (value == null) {
value = _nullValue;
}
_configurationFilterCache.put(filterCacheKey, value);
}
}
if (value instanceof String) {
return (String)value;
}
return null;
}
@Override
public String[] getArray(String key) {
Object value = _configurationArrayCache.get(key);
if (value == null) {
ComponentProperties componentProperties = getComponentProperties();
String[] array = componentProperties.getStringArray(key);
value = _fixArrayValue(array);
_configurationArrayCache.put(key, value);
}
if (value instanceof String[]) {
return (String[])value;
}
return _emptyArray;
}
@Override
public String[] getArray(String key, Filter filter) {
FilterCacheKey filterCacheKey = _buildFilterCacheKey(key, filter);
Object value = null;
if (filterCacheKey != null) {
value = _configurationFilterArrayCache.get(filterCacheKey);
}
if (value == null) {
ComponentProperties componentProperties = getComponentProperties();
String[] array = componentProperties.getStringArray(
key, getEasyConfFilter(filter));
value = _fixArrayValue(array);
if (filterCacheKey != null) {
_configurationFilterArrayCache.put(filterCacheKey, value);
}
}
if (value instanceof String[]) {
return (String[])value;
}
return _emptyArray;
}
@Override
public Properties getProperties() {
if (_properties != null) {
return _properties;
}
// For some strange reason, componentProperties.getProperties() returns
// values with spaces after commas. So a property setting of "xyz=1,2,3"
// actually returns "xyz=1, 2, 3". This can break applications that
// don't expect that extra space. However, getting the property value
// directly through componentProperties returns the correct value. This
// method fixes the weird behavior by returning properties with the
// correct values.
Properties properties = new Properties();
ComponentProperties componentProperties = getComponentProperties();
Properties componentPropertiesProperties =
componentProperties.getProperties();
for (String key : componentPropertiesProperties.stringPropertyNames()) {
properties.setProperty(key, componentProperties.getString(key));
}
_properties = properties;
return properties;
}
@Override
public Properties getProperties(String prefix, boolean removePrefix) {
Properties properties = getProperties();
return PropertiesUtil.getProperties(properties, prefix, removePrefix);
}
@Override
public void removeProperties(Properties properties) {
try {
ComponentProperties componentProperties = getComponentProperties();
ClassLoaderAggregateProperties classLoaderAggregateProperties =
(ClassLoaderAggregateProperties)
componentProperties.toConfiguration();
CompositeConfiguration compositeConfiguration =
classLoaderAggregateProperties.getBaseConfiguration();
Field field2 = CompositeConfiguration.class.getDeclaredField(
"configList");
field2.setAccessible(true);
@SuppressWarnings("unchecked")
List<Configuration> configurations =
(List<Configuration>)field2.get(compositeConfiguration);
Iterator<Configuration> itr = configurations.iterator();
while (itr.hasNext()) {
Configuration configuration = itr.next();
if (!(configuration instanceof MapConfiguration)) {
break;
}
MapConfiguration mapConfiguration =
(MapConfiguration)configuration;
if (mapConfiguration.getMap() == (Map<?, ?>)properties) {
itr.remove();
classLoaderAggregateProperties.removeConfiguration(
configuration);
}
}
_properties = null;
clearCache();
}
catch (Exception e) {
_log.error("The properties could not be removed", e);
}
}
@Override
public void set(String key, String value) {
ComponentProperties componentProperties = getComponentProperties();
componentProperties.setProperty(key, value);
clearCache();
}
protected ComponentProperties getComponentProperties() {
return _componentConfiguration.getProperties();
}
protected com.germinus.easyconf.Filter getEasyConfFilter(Filter filter) {
com.germinus.easyconf.Filter easyConfFilter =
com.germinus.easyconf.Filter.by(filter.getSelectors());
if (filter.getVariables() != null) {
easyConfFilter.setVariables(filter.getVariables());
}
return easyConfFilter;
}
protected void printSources(long companyId, String webId) {
ComponentProperties componentProperties = getComponentProperties();
List<String> sources = componentProperties.getLoadedSources();
for (int i = sources.size() - 1; i >= 0; i--) {
String source = sources.get(i);
if (_printedSources.contains(source)) {
continue;
}
_printedSources.add(source);
if (source.startsWith("bundleresource://")) {
continue;
}
String info = "Loading " + source;
if (companyId > CompanyConstants.SYSTEM) {
info +=
" for {companyId=" + companyId + ", webId=" + webId + "}";
}
System.out.println(info);
}
}
@SuppressWarnings("unchecked")
private static Map<String, Object> _castPropertiesToMap(
Properties properties) {
return (Map)properties;
}
private FilterCacheKey _buildFilterCacheKey(String key, Filter filter) {
if (filter.getVariables() != null) {
return null;
}
return new FilterCacheKey(key, filter);
}
private Object _fixArrayValue(String[] array) {
Object value = _nullValue;
if (ArrayUtil.isNotEmpty(array)) {
// Commons Configuration parses an empty property into a String
// array with one String containing one space. It also leaves a
// trailing array member if you set a property in more than one
// line.
if (Validator.isNull(array[array.length - 1])) {
String[] subarray = new String[array.length - 1];
System.arraycopy(array, 0, subarray, 0, subarray.length);
array = subarray;
}
if (array.length > 0) {
value = array;
}
}
return value;
}
private static final boolean _PRINT_DUPLICATE_CALLS_TO_GET = false;
private static final Log _log = LogFactoryUtil.getLog(
ConfigurationImpl.class);
private static final String[] _emptyArray = new String[0];
private static final Object _nullValue = new Object();
private final ComponentConfiguration _componentConfiguration;
private final Map<String, Object> _configurationArrayCache =
new ConcurrentHashMap<>();
private final Map<String, Object> _configurationCache =
new ConcurrentHashMap<>();
private final Map<FilterCacheKey, Object> _configurationFilterArrayCache =
new ConcurrentHashMap<>();
private final Map<FilterCacheKey, Object> _configurationFilterCache =
new ConcurrentHashMap<>();
private final Set<String> _printedSources = new HashSet<>();
private Properties _properties;
private static class FilterCacheKey {
@Override
public boolean equals(Object object) {
FilterCacheKey filterCacheKey = (FilterCacheKey)object;
if (Objects.equals(_key, filterCacheKey._key) &&
Arrays.equals(_selectors, filterCacheKey._selectors)) {
return true;
}
return false;
}
@Override
public int hashCode() {
int hashCode = HashUtil.hash(0, _key);
for (String selector : _selectors) {
hashCode = HashUtil.hash(hashCode, selector);
}
return hashCode;
}
private FilterCacheKey(String key, Filter filter) {
_key = key;
_selectors = filter.getSelectors();
}
private final String _key;
private final String[] _selectors;
}
}