blob: 30f3f505184082a0bdb8139a846f290c1bc32624 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.sling.installer.factories.configuration.impl;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
/**
* Utilities for configuration handling
*/
abstract class ConfigUtil {
/**
* This property marks the configuration as being deleted.
*/
public static final String PROPERTY_DELETE_MARKER = "org.apache.sling.installer.configuration.deleted";
/**
* This property has been used in older versions to keep track where the
* configuration has been installed from.
*/
private static final String CONFIG_PATH_KEY = "org.apache.sling.installer.osgi.path";
/**
* This property has been used in older versions to keep track of factory
* configurations.
*/
private static final String ALIAS_KEY = "org.apache.sling.installer.osgi.factoryaliaspid";
/** Configuration properties to ignore when comparing configs */
private static final Set<String> IGNORED_PROPERTIES = new HashSet<>();
static {
IGNORED_PROPERTIES.add(Constants.SERVICE_PID);
IGNORED_PROPERTIES.add(CONFIG_PATH_KEY);
IGNORED_PROPERTIES.add(ALIAS_KEY);
IGNORED_PROPERTIES.add(ConfigurationAdmin.SERVICE_FACTORYPID);
}
private static Set<String> collectKeys(final Dictionary<String, Object>a) {
final Set<String> keys = new HashSet<>();
final Enumeration<String> aI = a.keys();
while (aI.hasMoreElements() ) {
final String key = aI.nextElement();
if ( !IGNORED_PROPERTIES.contains(key) ) {
keys.add(key);
}
}
return keys;
}
/**
* Convert the object to an array
* @param value The array
* @return an object array
*/
private static Object[] convertToObjectArray(final Object value) {
final Object[] values = new Object[Array.getLength(value)];
for(int i=0;i<values.length;i++) {
values[i] = Array.get(value, i);
}
return values;
}
/** True if a and b represent the same config data, ignoring "non-configuration" keys in the dictionaries */
public static boolean isSameData(Dictionary<String, Object>a, Dictionary<String, Object>b) {
boolean result = false;
if (a != null && b != null) {
final Set<String> keysA = collectKeys(a);
final Set<String> keysB = collectKeys(b);
if ( keysA.size() == keysB.size() && keysA.containsAll(keysB) ) {
result = true;
for(final String key : keysA ) {
final Object valA = a.get(key);
final Object valB = b.get(key);
if ( !isSameValue(valA, valB) ) {
result = false;
break;
}
}
}
}
return result;
}
public static boolean isSameValue(final Object valA, final Object valB) {
if ( valA.getClass().isArray() && valB.getClass().isArray()) {
final Object[] arrA = convertToObjectArray(valA);
final Object[] arrB = convertToObjectArray(valB);
if ( arrA.length != arrB.length ) {
return false;
}
for(int i=0; i<arrA.length; i++) {
if ( !(String.valueOf(arrA[i]).equals(String.valueOf(arrB[i]))) ) {
return false;
}
}
} else if (!valA.getClass().isArray() && !valB.getClass().isArray()) {
// if no arrays do a string comparison
if ( !(String.valueOf(valA).equals(String.valueOf(valB))) ) {
return false;
}
} else {
// one value is array the other is not!
return false;
}
return true;
}
/**
* Remove all ignored properties
*/
public static Dictionary<String, Object> cleanConfiguration(final Dictionary<String, Object> config) {
final Dictionary<String, Object> cleanedConfig = new Hashtable<>();
final Enumeration<String> e = config.keys();
while(e.hasMoreElements()) {
final String key = e.nextElement();
if ( !IGNORED_PROPERTIES.contains(key) ) {
cleanedConfig.put(key, config.get(key));
}
}
return cleanedConfig;
}
/**
* Encode the value for the ldap filter: \, *, (, and ) should be escaped.
*/
private static String encode(final String value) {
return value.replace("\\", "\\\\")
.replace("*", "\\*")
.replace("(", "\\(")
.replace(")", "\\)");
}
public static Configuration getConfiguration(final ConfigurationAdmin ca,
final String factoryPid,
final String configPidOrName)
throws IOException, InvalidSyntaxException {
return getOrCreateConfiguration(ca, factoryPid, configPidOrName, null, false);
}
public static Configuration createConfiguration(final ConfigurationAdmin ca,
final String factoryPid,
final String configPidOrName,
final String location)
throws IOException, InvalidSyntaxException {
return getOrCreateConfiguration(ca, factoryPid, configPidOrName, location, true);
}
/**
*
* @param ca
* @param factoryPid
* @param configPidOrName
* @param location
* @param createIfNeeded
* @return
* @throws IOException - if access to persistent storage fails
* @throws InvalidSyntaxException
*/
private static Configuration getOrCreateConfiguration(final ConfigurationAdmin ca,
final String factoryPid,
final String configPidOrName,
final String location,
final boolean createIfNeeded)
throws IOException, InvalidSyntaxException {
Configuration result = null;
if (factoryPid == null) {
if (createIfNeeded) {
result = ca.getConfiguration(configPidOrName, location);
} else {
final String filter = "(" + Constants.SERVICE_PID + "=" + encode(configPidOrName)
+ ")";
final Configuration[] configs = ca.listConfigurations(filter);
if (configs != null && configs.length > 0) {
result = configs[0];
}
}
} else {
if (createIfNeeded) {
result = ca.getFactoryConfiguration(factoryPid, configPidOrName, location);
} else {
final String filter = "(&("
+ ConfigurationAdmin.SERVICE_FACTORYPID + "=" + encode(factoryPid)
+ ")("
+ Constants.SERVICE_PID + "=" + encode(ConfigUtil.getPIDOfFactoryPID(factoryPid, configPidOrName))
+ "))";
final Configuration[] configs = ca.listConfigurations(filter);
if (configs != null && configs.length > 0) {
result = configs[0];
}
}
}
return result;
}
public static Configuration getLegacyFactoryConfig(final ConfigurationAdmin ca,
final String factoryPid,
final String aliasPid,
final String pid)
throws IOException, InvalidSyntaxException {
final String configPid = (aliasPid != null ? aliasPid.substring(factoryPid.length() + 1) : pid);
Configuration result = null;
Configuration configs[] = null;
if ( configPid != null ) {
configs = ca.listConfigurations("(&("
+ ConfigurationAdmin.SERVICE_FACTORYPID + "=" + encode(factoryPid)
+ ")(" + Constants.SERVICE_PID + "=" + encode(configPid)
+ "))");
}
if (configs == null || configs.length == 0) {
configs = ca.listConfigurations("(&("
+ ConfigurationAdmin.SERVICE_FACTORYPID + "=" + encode(factoryPid)
+ ")(" + Constants.SERVICE_PID + "=" + encode(factoryPid + "." + configPid)
+ "))");
}
if (configs == null || configs.length == 0) {
// check for old style with alias pid
configs = ca.listConfigurations(
"(&(" + ConfigurationAdmin.SERVICE_FACTORYPID
+ "=" + factoryPid + ")(" + ALIAS_KEY + "=" + encode(configPid)
+ "))");
if (configs != null && configs.length > 0) {
result = configs[0];
}
} else {
result = configs[0];
}
return result;
}
public static boolean toBoolean(final Object obj, final boolean defaultValue) {
boolean result = defaultValue;
if ( obj != null ) {
if (obj instanceof Boolean) {
result = ((Boolean) obj).booleanValue();
} else {
result = Boolean.valueOf(String.valueOf(obj));
}
}
return result;
}
/**
* Get the PID for a factory PID by using the R7 format
* @param factoryPID The factory pid
* @param name The name
* @return The PID
*/
public static String getPIDOfFactoryPID(final String factoryPID, final String name) {
return factoryPID.concat("~").concat(name);
}
/**
* Merge all dictionaries into a single dictionary in reverse order
* @param propertiesList The list of dictionaries
* @return The merged dictionary
*/
public static Dictionary<String, Object> mergeReverseOrder(final List<Dictionary<String, Object>> propertiesList) {
Collections.reverse(propertiesList);
final Dictionary<String, Object> properties = new Hashtable<>();
for(final Dictionary<String, Object> dict : propertiesList) {
merge(properties, dict);
}
return properties;
}
/**
* Merge one dictionary into the other
* @param base Base dictionary
* @param props Overwriting dictionary
*/
private static void merge(final Dictionary<String, Object> base, final Dictionary<String, Object> props) {
final Enumeration<String> keyIter = props.keys();
while (keyIter.hasMoreElements() ) {
final String key = keyIter.nextElement();
base.put(key, props.get(key));
}
}
}