/* * 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.felix.ipojo; import org.apache.felix.ipojo.extender.internal.Extender; import org.apache.felix.ipojo.util.Log; import org.apache.felix.ipojo.util.Logger; import org.apache.felix.ipojo.util.ServiceLocator; import org.osgi.framework.*; import org.osgi.service.cm.*; import org.osgi.service.cm.ConfigurationException; import java.io.IOException; import java.util.*; /** * An object tracking configuration from the configuration admin. It delegates to the underlying factories or * component instance the action. * <p/> * This class implements a Configuration Listener, so events are received asynchronously. */ public class ConfigurationTracker implements ConfigurationListener { /** * The tracker instance. */ private static ConfigurationTracker m_singleton; private final ServiceRegistration m_registration; private final BundleContext m_context; private final Logger m_logger; private Map<String, IPojoFactory> m_factories = new HashMap<String, IPojoFactory>(); public ConfigurationTracker() { m_context = Extender.getIPOJOBundleContext(); m_logger = new Logger(m_context, "iPOJO Configuration Admin listener", Log.INFO); // register as listener for configurations Dictionary<String, Object> props = new Hashtable<String, Object>(); props.put(Constants.SERVICE_DESCRIPTION, "iPOJO Configuration Admin Listener"); props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); m_registration = m_context.registerService(ConfigurationListener.class.getName(), this, props); } public static void initialize() { synchronized (ConfigurationTracker.class) { if (m_singleton == null) { m_singleton = new ConfigurationTracker(); } } } public static void shutdown() { m_singleton.dispose(); m_singleton = null; } public static ConfigurationTracker get() { return m_singleton; } /** * This method must be called by the iPOJO System itself, and only once. */ public synchronized void dispose() { if (m_registration != null) { m_registration.unregister(); } m_factories.clear(); } public synchronized void registerFactory(IPojoFactory factory) { m_factories.put(factory.getFactoryName(), factory); ServiceLocator<ConfigurationAdmin> locator = new ServiceLocator<ConfigurationAdmin>(ConfigurationAdmin .class, m_context); final ConfigurationAdmin admin = locator.get(); if (admin == null) { return; } List<Configuration> configurations = findFactoryConfiguration(admin, factory); for (Configuration configuration : configurations) { try { factory.updated(configuration.getPid(), configuration.getProperties()); } catch (ConfigurationException e) { m_logger.log(Log.ERROR, "Cannot reconfigure instance " + configuration.getPid() + " from " + configuration.getFactoryPid() + " with the configuration : " + configuration.getProperties(), e); } } } public synchronized void instanceCreated(ComponentInstance instance) { ServiceLocator<ConfigurationAdmin> locator = new ServiceLocator<ConfigurationAdmin>(ConfigurationAdmin .class, m_context); final ConfigurationAdmin admin = locator.get(); if (admin == null) { return; } Configuration configuration = findSingletonConfiguration(admin, instance.getInstanceName()); if (configuration != null) { Hashtable<String, Object> conf = copyConfiguration(configuration); if (!conf.containsKey(Factory.INSTANCE_NAME_PROPERTY)) { conf.put(Factory.INSTANCE_NAME_PROPERTY, configuration.getPid()); } try { instance.getFactory().reconfigure(conf); } catch (UnacceptableConfiguration unacceptableConfiguration) { m_logger.log(Log.ERROR, "Cannot reconfigure the instance " + configuration.getPid() + " - the " + "configuration is unacceptable", unacceptableConfiguration); } catch (MissingHandlerException e) { m_logger.log(Log.ERROR, "Cannot reconfigure the instance " + configuration.getPid() + " - factory is " + "invalid", e); } } } public synchronized void unregisterFactory(IPojoFactory factory) { m_factories.remove(factory.getFactoryName()); } public void configurationEvent(ConfigurationEvent event) { String pid = event.getPid(); String factoryPid = event.getFactoryPid(); if (factoryPid == null) { ComponentInstance instance = retrieveInstance(pid); if (instance != null) { manageConfigurationEventForSingleton(instance, event); } } else { IPojoFactory factory = retrieveFactory(factoryPid); if (factory != null) { manageConfigurationEventForFactory(factory, event); } // Else the factory is unknown, do nothing. } } private void manageConfigurationEventForFactory(final IPojoFactory factory, final ConfigurationEvent event) { ServiceLocator<ConfigurationAdmin> locator = new ServiceLocator<ConfigurationAdmin>(ConfigurationAdmin .class, m_context); switch (event.getType()) { case ConfigurationEvent.CM_DELETED: factory.deleted(event.getPid()); break; case ConfigurationEvent.CM_UPDATED: final ConfigurationAdmin admin = locator.get(); if (admin == null) { break; } final Configuration config = getConfiguration(admin, event.getPid(), factory.getBundleContext().getBundle()); if (config != null) { try { factory.updated(event.getPid(), config.getProperties()); } catch (org.osgi.service.cm.ConfigurationException e) { m_logger.log(Log.ERROR, "Cannot reconfigure instance " + event.getPid() + " with the new " + "configuration " + config.getProperties(), e); } } default: // To nothing. } locator.unget(); } private void manageConfigurationEventForSingleton(final ComponentInstance instance, final ConfigurationEvent event) { ServiceLocator<ConfigurationAdmin> locator = new ServiceLocator<ConfigurationAdmin>(ConfigurationAdmin .class, m_context); switch (event.getType()) { case ConfigurationEvent.CM_DELETED: instance.dispose(); break; case ConfigurationEvent.CM_UPDATED: final ConfigurationAdmin admin = locator.get(); if (admin == null) { break; } final Configuration config = getConfiguration(admin, event.getPid(), instance.getFactory().getBundleContext().getBundle()); if (config != null) { Hashtable<String, Object> conf = copyConfiguration(config); if (!conf.containsKey(Factory.INSTANCE_NAME_PROPERTY)) { conf.put(Factory.INSTANCE_NAME_PROPERTY, event.getPid()); } try { instance.getFactory().reconfigure(conf); } catch (UnacceptableConfiguration unacceptableConfiguration) { m_logger.log(Log.ERROR, "Cannot reconfigure the instance " + event.getPid() + " - the " + "configuration is unacceptable", unacceptableConfiguration); } catch (MissingHandlerException e) { m_logger.log(Log.ERROR, "Cannot reconfigure the instance " + event.getPid() + " - factory is " + "invalid", e); } } default: // To nothing. } locator.unget(); } private Hashtable<String, Object> copyConfiguration(Configuration config) { Hashtable<String, Object> conf = new Hashtable<String, Object>(); // Copy configuration Enumeration keys = config.getProperties().keys(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); conf.put(key, config.getProperties().get(key)); } return conf; } private IPojoFactory retrieveFactory(String factoryPid) { synchronized (this) { return m_factories.get(factoryPid); } } private ComponentInstance retrieveInstance(String instanceName) { Collection<IPojoFactory> factories; synchronized (this) { factories = m_factories.values(); } for (IPojoFactory factory : factories) { ComponentInstance instance = factory.getInstanceByName(instanceName); if (instance != null) { return instance; } } return null; } private Configuration getConfiguration(final ConfigurationAdmin admin, final String pid, final Bundle bundle) { if (admin == null) { return null; } try { // Even if it is possible, we don't build the filter with bundle.location to detect the case where the // configuration exists but can't be managed by iPOJO. final Configuration cfg = admin.getConfiguration(pid); final String bundleLocation = bundle.getLocation(); if (cfg.getBundleLocation() == null || bundleLocation.equals(cfg.getBundleLocation()) || m_context.getBundle().getLocation().equals(cfg.getBundleLocation())) { cfg.setBundleLocation(bundleLocation); return cfg; } // Multi-location if (cfg.getBundleLocation().startsWith("?")) { if (bundle.hasPermission(new ConfigurationPermission(cfg.getBundleLocation(), "target"))) { return cfg; } } // configuration belongs to another bundle, cannot be used here m_logger.log(Log.ERROR, "Cannot use configuration pid=" + pid + " for bundle " + bundleLocation + " because it belongs to bundle " + cfg.getBundleLocation()); } catch (IOException ioe) { m_logger.log(Log.WARNING, "Failed reading configuration for pid=" + pid, ioe); } return null; } public List<Configuration> findFactoryConfiguration(final ConfigurationAdmin admin, final IPojoFactory factory) { final String filter = "(service.factoryPid=" + factory.getFactoryName() + ")"; return findConfigurations(admin, filter); } public Configuration findSingletonConfiguration(final ConfigurationAdmin admin, final String pid) { final String filter = "(service.pid=" + pid + ")"; List<Configuration> list = findConfigurations(admin, filter); if (list.isEmpty()) { return null; } else { return list.get(0); } } private List<Configuration> findConfigurations(final ConfigurationAdmin admin, final String filter) { List<Configuration> configurations = Collections.emptyList(); if (admin == null) { return configurations; } try { Configuration[] list = admin.listConfigurations(filter); if (list == null) { return configurations; } else { return Arrays.asList(list); } } catch (InvalidSyntaxException e) { m_logger.log(Log.ERROR, "Invalid Configuration selection filter " + filter, e); } catch (IOException e) { m_logger.log(Log.ERROR, "Error when retrieving configurations for filter=" + filter, e); } return configurations; } }