/** * 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.aries.blueprint.compendium.cm; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Dictionary; import java.util.Enumeration; import java.util.Hashtable; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.service.cm.Configuration; import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.cm.ConfigurationPlugin; public class CmUtils { private CmUtils() { } public static Configuration getConfiguration(ConfigurationAdmin configAdmin, String persistentId) throws IOException { String filter = '(' + Constants.SERVICE_PID + '=' + persistentId + ')'; Configuration[] configs; try { configs = configAdmin.listConfigurations(filter); } catch (InvalidSyntaxException e) { // this should not happen throw new RuntimeException("Invalid filter: " + filter); } if (configs != null && configs.length > 0) { return configs[0]; } else { // TODO: what should we do? // throw new RuntimeException("No configuration object for pid=" + persistentId); return null; } } public static Dictionary<String, Object> getProperties(ServiceReference service, String persistentId) throws IOException { BundleContext bc = service.getBundle().getBundleContext(); ServiceReference<ConfigurationAdmin> caRef = bc.getServiceReference(ConfigurationAdmin.class); try { ConfigurationAdmin ca = bc.getService(caRef); Configuration config = getConfiguration(ca, persistentId); if (config != null) { Dictionary<String, Object> props = new CaseInsensitiveDictionary(config.getProperties()); Bundle bundle = caRef.getBundle(); if (bundle != null) { BundleContext caBc = bundle.getBundleContext(); if (caBc != null) { try { callPlugins(caBc, props, service, persistentId, null); } catch (IllegalStateException ise) { // we don't care it doesn't exist so, shrug. } } } return props; } else { return null; } } finally { bc.ungetService(caRef); } } private static void callPlugins(final BundleContext bundleContext, final Dictionary<String, Object> props, final ServiceReference sr, final String configPid, final String factoryPid) { ServiceReference[] plugins = null; try { final String targetPid = (factoryPid == null) ? configPid : factoryPid; String filter = "(|(!(cm.target=*))(cm.target=" + targetPid + "))"; plugins = bundleContext.getServiceReferences(ConfigurationPlugin.class.getName(), filter); } catch (InvalidSyntaxException ise) { // no filter, no exception ... } // abort early if there are no plugins if (plugins == null || plugins.length == 0) { return; } // sort the plugins by their service.cmRanking if (plugins.length > 1) { Arrays.sort(plugins, CM_RANKING); } // call the plugins in order for (ServiceReference pluginRef : plugins) { ConfigurationPlugin plugin = (ConfigurationPlugin) bundleContext.getService(pluginRef); if (plugin != null) { try { plugin.modifyConfiguration(sr, props); } catch (Throwable t) { // Ignore } finally { // ensure ungetting the plugin bundleContext.ungetService(pluginRef); } setAutoProperties(props, configPid, factoryPid); } } } private static void setAutoProperties( Dictionary<String, Object> properties, String pid, String factoryPid ) { replaceProperty(properties, Constants.SERVICE_PID, pid); replaceProperty(properties, ConfigurationAdmin.SERVICE_FACTORYPID, factoryPid); properties.remove(ConfigurationAdmin.SERVICE_BUNDLELOCATION); } private static void replaceProperty(Dictionary<String, Object> properties, String key, String value) { if (value == null) { properties.remove(key); } else { properties.put(key, value); } } private static Comparator<ServiceReference> CM_RANKING = new Comparator<ServiceReference>() { @Override public int compare(ServiceReference sr1, ServiceReference sr2) { final long rank1 = getLong(sr1, ConfigurationPlugin.CM_RANKING); final long rank2 = getLong(sr2, ConfigurationPlugin.CM_RANKING); if (rank1 == rank2) { return 0; } return (rank1 < rank2) ? -1 : 1; } protected long getLong(ServiceReference sr, String property) { Object rankObj = sr.getProperty(property); if (rankObj instanceof Number) { return ((Number) rankObj).longValue(); } return 0; } }; private static class CaseInsensitiveDictionary extends Dictionary<String, Object> { private final Hashtable<String, Object> internalMap = new Hashtable<String, Object>(); private final Hashtable<String, String> originalKeys = new Hashtable<String, String>(); public CaseInsensitiveDictionary(Dictionary<String, Object> props) { if (props != null) { Enumeration<String> keys = props.keys(); while (keys.hasMoreElements()) { // check the correct syntax of the key String key = checkKey(keys.nextElement()); // check uniqueness of key String lowerCase = key.toLowerCase(); if (internalMap.containsKey(lowerCase)) { throw new IllegalArgumentException("Key [" + key + "] already present in different case"); } // check the value Object value = props.get(key); checkValue(value); // add the key/value pair internalMap.put(lowerCase, value); originalKeys.put(lowerCase, key); } } } public Enumeration<Object> elements() { return Collections.enumeration(internalMap.values()); } public Object get(Object keyObj) { String lowerCase = checkKey(keyObj == null ? null : keyObj.toString()).toLowerCase(); return internalMap.get(lowerCase); } public boolean isEmpty() { return internalMap.isEmpty(); } public Enumeration<String> keys() { return Collections.enumeration(originalKeys.values()); } public Object put(String key, Object value) { String lowerCase = checkKey(key).toLowerCase(); checkValue(value); originalKeys.put(lowerCase, key); return internalMap.put(lowerCase, value); } public Object remove(Object keyObj) { String lowerCase = checkKey(keyObj == null ? null : keyObj.toString()).toLowerCase(); originalKeys.remove(lowerCase); return internalMap.remove(lowerCase); } public int size() { return internalMap.size(); } static String checkKey(String key) { if (key == null || key.length() == 0) { throw new IllegalArgumentException("Key must not be null nor an empty string"); } return key; } static Object checkValue(Object value) { if (value == null) { throw new IllegalArgumentException("Value must not be null"); } return value; } public String toString() { return internalMap.toString(); } } }