blob: 04fd5921f66bfe5585e500d60a0c73184ec4f2f7 [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.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;
}
}