blob: 9a8efe4807097cfeebe44a20b485df42d27f8339 [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.jackrabbit.oak.run.osgi;
import java.io.IOException;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.felix.utils.properties.InterpolationHelper;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class ConfigInstaller {
private static final String MARKER_NAME = "oak.configinstall.name";
private final Logger log = LoggerFactory.getLogger(getClass());
private final ConfigurationAdmin cm;
private final BundleContext bundleContext;
public ConfigInstaller(ConfigurationAdmin cm, BundleContext bundleContext) {
this.cm = cm;
this.bundleContext = bundleContext;
}
public void installConfigs(Map<String, Map<String, Object>> osgiConfig)
throws Exception {
for (Map.Entry<String, Map<String, Object>> pidEntry : osgiConfig.entrySet()) {
final String pidString = pidEntry.getKey();
final Hashtable<String, Object> current = new Hashtable<String, Object>();
current.putAll(pidEntry.getValue());
performSubstitution(current);
String[] pid = parsePid(pidString);
Configuration config = getConfiguration(pidString, pid[0], pid[1]);
@SuppressWarnings("unchecked") Dictionary<String, Object> props = config.getProperties();
Hashtable<String, Object> old = props != null ?
new Hashtable<String, Object>(new DictionaryAsMap<String, Object>(props)) : null;
if (old != null) {
old.remove(MARKER_NAME);
old.remove(Constants.SERVICE_PID);
old.remove(ConfigurationAdmin.SERVICE_FACTORYPID);
}
if (!current.equals(old)) {
current.put(MARKER_NAME, pidString);
if (config.getBundleLocation() != null) {
config.setBundleLocation(null);
}
if (old == null) {
log.info("Creating configuration from {}", pidString);
} else {
log.info("Updating configuration from {}", pidString);
}
config.update(current);
}
}
}
public void removeConfigs(Set<String> pidsToBeRemoved) throws Exception {
for (String pidString : pidsToBeRemoved) {
String[] pid = parsePid(pidString);
Configuration config = getConfiguration(pidString, pid[0], pid[1]);
config.delete();
}
if (!pidsToBeRemoved.isEmpty()) {
log.info("Configuration belonging to following pids have been removed {}", pidsToBeRemoved);
}
}
/**
* Determines the existing configs which are installed by ConfigInstaller
*
* @return set of pid strings
*/
public Set<String> determineExistingConfigs() throws IOException, InvalidSyntaxException {
Set<String> pids = Sets.newHashSet();
String filter = "(" + MARKER_NAME + "=" + "*" + ")";
Configuration[] configurations = cm.listConfigurations(filter);
if (configurations != null) {
for (Configuration cfg : configurations) {
pids.add((String) cfg.getProperties().get(MARKER_NAME));
}
}
return pids;
}
private void performSubstitution(Hashtable<String, Object> current) {
Map<String, String> simpleConfig = Maps.newHashMap();
for (Map.Entry<String, Object> e : current.entrySet()) {
if (e.getValue() instanceof String) {
simpleConfig.put(e.getKey(), (String) e.getValue());
}
}
InterpolationHelper.performSubstitution(simpleConfig, bundleContext);
for (Map.Entry<String, String> e : simpleConfig.entrySet()) {
current.put(e.getKey(), e.getValue());
}
}
private Configuration getConfiguration(String pidString, String pid, String factoryPid)
throws Exception {
Configuration oldConfiguration = findExistingConfiguration(pidString);
if (oldConfiguration != null) {
return oldConfiguration;
} else {
Configuration newConfiguration;
if (factoryPid != null) {
newConfiguration = cm.createFactoryConfiguration(pid, null);
} else {
newConfiguration = cm.getConfiguration(pid, null);
}
return newConfiguration;
}
}
private Configuration findExistingConfiguration(String pidString) throws Exception {
String filter = "(" + MARKER_NAME + "=" + escapeFilterValue(pidString) + ")";
Configuration[] configurations = cm.listConfigurations(filter);
if (configurations != null && configurations.length > 0) {
return configurations[0];
} else {
return null;
}
}
private static String escapeFilterValue(String s) {
return s.replaceAll("[(]", "\\\\(").
replaceAll("[)]", "\\\\)").
replaceAll("[=]", "\\\\=").
replaceAll("[\\*]", "\\\\*");
}
private static String[] parsePid(String pid) {
int n = pid.indexOf('-');
if (n > 0) {
String factoryPid = pid.substring(n + 1);
pid = pid.substring(0, n);
return new String[]
{
pid, factoryPid
};
} else {
return new String[]
{
pid, null
};
}
}
}