blob: 64067be2dbfd8e6847345e99637e828d4742f8ee [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.scr.impl.config;
import java.io.IOException;
import java.util.Dictionary;
import java.util.Hashtable;
import org.apache.felix.scr.impl.Activator;
import org.apache.felix.scr.impl.BundleComponentActivator;
import org.apache.felix.scr.impl.ComponentRegistry;
import org.apache.felix.scr.impl.manager.ComponentFactoryImpl;
import org.apache.felix.scr.impl.metadata.ComponentMetadata;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.ConfigurationEvent;
import org.osgi.service.cm.ConfigurationListener;
import org.osgi.service.log.LogService;
public class ConfigurationComponentRegistry extends ComponentRegistry implements ServiceListener, ConfigurationListener
{
// the name of the ConfigurationAdmin service
private static final String CONFIGURATION_ADMIN = "org.osgi.service.cm.ConfigurationAdmin";
// the service m_registration of the ConfigurationListener service
private ServiceRegistration m_registration;
// the bundle context
private BundleContext m_bundleContext;
public ConfigurationComponentRegistry( final BundleContext context )
{
super( context );
m_bundleContext = context;
// register as listener for configurations
Dictionary props = new Hashtable();
props.put( Constants.SERVICE_DESCRIPTION, "Declarative Services Configuration Support Listener" );
props.put( Constants.SERVICE_VENDOR, "The Apache Software Foundation" );
m_registration = context.registerService( new String[]
{ ConfigurationListener.class.getName() }, this, props );
// keep me informed on ConfigurationAdmin state changes
try
{
context.addServiceListener( this, "(objectclass=" + CONFIGURATION_ADMIN + ")" );
}
catch ( InvalidSyntaxException ise )
{
// not expected (filter is tested valid)
}
}
public void dispose()
{
m_bundleContext.removeServiceListener( this );
if ( m_registration != null )
{
m_registration.unregister();
m_registration = null;
}
super.dispose();
}
//---------- BaseConfigurationSupport overwrites
public ComponentHolder createComponentHolder( final BundleComponentActivator activator,
final ComponentMetadata metadata )
{
// 112.7 configure unless configuration not required
if ( metadata.isConfigurationIgnored() )
{
return super.createComponentHolder( activator, metadata );
}
// prepare the configuration holder
final ComponentHolder holder;
if ( metadata.isFactory() )
{
holder = new ComponentFactoryImpl( activator, metadata );
}
else
{
holder = new ConfiguredComponentHolder( activator, metadata );
}
final BundleContext bundleContext = activator.getBundleContext();
final String bundleLocation = bundleContext.getBundle().getLocation();
final String name = metadata.getName();
final ServiceReference caRef = bundleContext.getServiceReference( CONFIGURATION_ADMIN );
if ( caRef != null )
{
final ConfigurationAdmin ca = ( ConfigurationAdmin ) bundleContext.getService( caRef );
if ( ca != null )
{
try
{
final Configuration[] factory = findFactoryConfigurations( ca, name );
if ( factory != null )
{
for ( int i = 0; i < factory.length; i++ )
{
final String pid = factory[i].getPid();
final Dictionary props = getConfiguration( ca, pid, bundleLocation );
holder.configurationUpdated( pid, props );
}
}
else
{
// check for configuration and configure the holder
final Configuration singleton = findSingletonConfiguration( ca, name );
if ( singleton != null )
{
final Dictionary props = getConfiguration( ca, name, bundleLocation );
holder.configurationUpdated( name, props );
}
}
}
finally
{
bundleContext.ungetService( caRef );
}
}
}
return holder;
}
//---------- ServiceListener
/**
* Called if the Configuration Admin service changes state. This
* implementation is mainly interested in the Configuration Admin service
* being registered <i>after</i> the Declarative Services setup to be able
* to forward existing configuration.
*
* @param event The service change event
*/
public void serviceChanged( ServiceEvent event )
{
if ( event.getType() == ServiceEvent.REGISTERED )
{
Configuration[] configs = null;
final ServiceReference caRef = event.getServiceReference();
final Object service = m_bundleContext.getService( caRef );
try
{
if ( service instanceof ConfigurationAdmin )
{
configs = findConfigurations( ( ConfigurationAdmin ) service, null );
}
}
finally
{
if ( service != null )
{
m_bundleContext.ungetService( caRef );
}
}
if ( configs != null )
{
for ( int i = 0; i < configs.length; i++ )
{
ConfigurationEvent cfgEvent = new ConfigurationEvent( caRef, ConfigurationEvent.CM_UPDATED,
configs[i].getFactoryPid(), configs[i].getPid() );
configurationEvent( cfgEvent );
}
}
}
}
//---------- ConfigurationListener
/**
* Called by the Configuration Admin service if a configuration is updated
* or removed.
* <p>
* This method is really only called upon configuration changes; it is not
* called for existing configurations upon startup of the Configuration
* Admin service. To bridge this gap, the
* {@link #serviceChanged(ServiceEvent)} method called when the
* Configuration Admin service is registered calls this method for all
* existing configurations to be able to foward existing configurations
* to components.
*
* @param event The configuration change event
*/
public void configurationEvent( ConfigurationEvent event )
{
final String pid = event.getPid();
final String factoryPid = event.getFactoryPid();
final ComponentHolder cm;
if ( factoryPid == null )
{
cm = getComponentHolder( pid );
}
else
{
cm = getComponentHolder( factoryPid );
}
Activator.log( LogService.LOG_DEBUG, null, "configurationEvent: Handling "
+ ( ( event.getType() == ConfigurationEvent.CM_DELETED ) ? "DELETE" : "UPDATE" ) + " of Configuration PID="
+ pid, null );
if ( cm != null && !cm.getComponentMetadata().isConfigurationIgnored() )
{
switch ( event.getType() )
{
case ConfigurationEvent.CM_DELETED:
cm.configurationDeleted( pid );
break;
case ConfigurationEvent.CM_UPDATED:
final BundleComponentActivator activator = cm.getActivator();
if ( activator == null )
{
break;
}
final BundleContext bundleContext = activator.getBundleContext();
if ( bundleContext == null )
{
break;
}
final ServiceReference caRef = bundleContext.getServiceReference( CONFIGURATION_ADMIN );
if ( caRef != null )
{
try
{
final ConfigurationAdmin ca = ( ConfigurationAdmin ) bundleContext.getService( caRef );
if ( ca != null )
{
try
{
final Dictionary dict = getConfiguration( ca, pid, bundleContext.getBundle()
.getLocation() );
if ( dict != null )
{
cm.configurationUpdated( pid, dict );
}
}
finally
{
bundleContext.ungetService( caRef );
}
}
}
catch ( IllegalStateException ise )
{
// If the bundle has been stopped conurrently
}
}
break;
default:
Activator.log( LogService.LOG_WARNING, null, "Unknown ConfigurationEvent type " + event.getType(),
null );
}
}
}
private Dictionary getConfiguration( final ConfigurationAdmin ca, final String pid, final String bundleLocation )
{
try
{
final Configuration cfg = ca.getConfiguration( pid );
if ( bundleLocation.equals( cfg.getBundleLocation() ) || Activator.hasCtWorkaround() )
{
return cfg.getProperties();
}
// configuration belongs to another bundle, cannot be used here
Activator.log( LogService.LOG_ERROR, null, "Cannot use configuration pid=" + pid + " for bundle "
+ bundleLocation + " because it belongs to bundle " + cfg.getBundleLocation(), null );
}
catch ( IOException ioe )
{
Activator.log( LogService.LOG_WARNING, null, "Failed reading configuration for pid=" + pid, ioe );
}
return null;
}
/**
* Returns the configuration whose PID equals the given pid. If no such
* configuration exists, <code>null</code> is returned.
* @param ctx
* @param pid
* @return
*/
public Configuration findSingletonConfiguration( final ConfigurationAdmin ca, final String pid )
{
final String filter = "(service.pid=" + pid + ")";
final Configuration[] cfg = findConfigurations( ca, filter );
return ( cfg == null || cfg.length == 0 ) ? null : cfg[0];
}
/**
* Returns all configurations whose factory PID equals the given factory PID
* or <code>null</code> if no such configurations exist
* @param ctx
* @param factoryPid
* @return
*/
public Configuration[] findFactoryConfigurations( final ConfigurationAdmin ca, final String factoryPid )
{
final String filter = "(service.factoryPid=" + factoryPid + ")";
return findConfigurations( ca, filter );
}
private Configuration[] findConfigurations( final ConfigurationAdmin ca, final String filter )
{
try
{
return ca.listConfigurations( filter );
}
catch ( IOException ioe )
{
Activator.log( LogService.LOG_WARNING, null, "Problem listing configurations for filter=" + filter, ioe );
}
catch ( InvalidSyntaxException ise )
{
Activator.log( LogService.LOG_ERROR, null, "Invalid Configuration selection filter " + filter, ise );
}
// no factories in case of problems
return null;
}
}