FELIX-3176 Implement Configuration Admin 4.1 changes:
- CM_LOCATION_CHANGED event
- reassign config on location changes
- support for new configuration Regions
- Factory (internal mechanism) are not location bound any more
- Each factory configuration instance is individually bound
- adapt integration tests
- temporarily include ConfigurationEvent and ConfigurationPermission
classes from the OSGi repo for two new constants
- export cm package at version 1.4 (and reimport [1.4,1.5))
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1186765 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/configadmin/pom.xml b/configadmin/pom.xml
index e3abe64..fe87aab 100644
--- a/configadmin/pom.xml
+++ b/configadmin/pom.xml
@@ -32,7 +32,7 @@
<name>Apache Felix Configuration Admin Service</name>
<description>
- Implementation of the OSGi Configuration Admin Service Specification 1.2
+ Implementation of the OSGi Configuration Admin Service Specification 1.4
</description>
<scm>
@@ -149,13 +149,14 @@
<Export-Package>
org.apache.felix.cm;
org.apache.felix.cm.file;version=${api-package-version};provide:=true,
- org.osgi.service.cm;provide:=true
+ org.osgi.service.cm;version=1.4;-split-package:=merge-first;provide:=true
</Export-Package>
<Private-Package>
org.apache.felix.cm.impl,
org.osgi.util.tracker
</Private-Package>
<Import-Package>
+ org.osgi.service.cm;version="[1.4,1.5)",
*
</Import-Package>
<DynamicImport-Package>
@@ -163,7 +164,7 @@
</DynamicImport-Package>
<Export-Service>
org.osgi.service.cm.ConfigurationAdmin;
- service.description="Configuration Admin Service Specification 1.3 Implementation";
+ service.description="Configuration Admin Service Specification 1.4 Implementation";
service.pid="org.osgi.service.cm.ConfigurationAdmin";
service.vendor="Apache Software Foundation",
org.apache.felix.cm.PersistenceManager;
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java b/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java
index 7c118ca..b4076c7 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java
@@ -63,28 +63,31 @@
{
this();
- Enumeration keys = props.keys();
- while ( keys.hasMoreElements() )
+ if ( props != null )
{
- Object key = keys.nextElement();
-
- // check the correct syntax of the key
- checkKey( key );
-
- // check uniqueness of key
- String lowerCase = ( ( String ) key ).toLowerCase();
- if ( internalMap.containsKey( lowerCase ) )
+ Enumeration keys = props.keys();
+ while ( keys.hasMoreElements() )
{
- throw new IllegalArgumentException( "Key [" + key + "] already present in different case" );
+ Object key = keys.nextElement();
+
+ // check the correct syntax of the key
+ checkKey( key );
+
+ // check uniqueness of key
+ String lowerCase = ( ( String ) key ).toLowerCase();
+ if ( internalMap.containsKey( lowerCase ) )
+ {
+ throw new IllegalArgumentException( "Key [" + key + "] already present in different case" );
+ }
+
+ // check the value
+ Object value = props.get( key );
+ value = checkValue( value );
+
+ // add the key/value pair
+ internalMap.put( lowerCase, value );
+ originalKeys.put( lowerCase, key );
}
-
- // check the value
- Object value = props.get( key );
- value = checkValue( value );
-
- // add the key/value pair
- internalMap.put( lowerCase, value );
- originalKeys.put( lowerCase, key );
}
}
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java
index b62b133..396f564 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java
@@ -70,7 +70,8 @@
*/
public String getBundleLocation()
{
- configurationAdmin.checkPermission();
+ // CM 1.4 / 104.13.2.4
+ configurationAdmin.checkPermission( delegatee.getBundleLocation() );
checkDeleted();
return delegatee.getBundleLocation();
}
@@ -82,7 +83,9 @@
*/
public void setBundleLocation( String bundleLocation )
{
- configurationAdmin.checkPermission();
+ // CM 1.4 / 104.13.2.4
+ configurationAdmin.checkPermission( delegatee.getBundleLocation() );
+ configurationAdmin.checkPermission( bundleLocation );
checkDeleted();
delegatee.setStaticBundleLocation( bundleLocation );
}
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java
index d5d4517..73bf15a 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java
@@ -79,7 +79,8 @@
*/
public Configuration createFactoryConfiguration( String factoryPid, String location ) throws IOException
{
- this.checkPermission();
+ // CM 1.4 / 104.13.2.3
+ this.checkPermission( location );
return this.wrap( configurationManager.createFactoryConfiguration( factoryPid, location ) );
}
@@ -98,7 +99,8 @@
}
else if ( !config.getBundleLocation().equals( this.getBundle().getLocation() ) )
{
- this.checkPermission();
+ // CM 1.4 / 104.13.2.3
+ this.checkPermission( config.getBundleLocation() );
}
return this.wrap( config );
@@ -110,9 +112,17 @@
*/
public Configuration getConfiguration( String pid, String location ) throws IOException
{
- this.checkPermission();
+ // CM 1.4 / 104.13.2.3
+ this.checkPermission( location );
- return this.wrap( configurationManager.getConfiguration( pid, location ) );
+ ConfigurationImpl config = configurationManager.getConfiguration( pid, location );
+ if ( config.getBundleLocation() != null )
+ {
+ // CM 1.4 / 104.13.2.3
+ this.checkPermission( config.getBundleLocation() );
+ }
+
+ return this.wrap( config );
}
@@ -149,11 +159,11 @@
* Returns <code>true</code> if the current access control context (call
* stack) has the CONFIGURE permission.
*/
- boolean hasPermission()
+ boolean hasPermission(String name)
{
try
{
- checkPermission();
+ checkPermission(name);
return true;
}
catch ( SecurityException se )
@@ -164,20 +174,30 @@
/**
- * Checks whether the current access control context (call stack) has the
- * <code>CONFIGURE</code> permission and throws a
+ * Checks whether the current access control context (call stack) has
+ * the given permission for the given bundle location and throws a
* <code>SecurityException</code> if this is not the case.
*
- * @throws SecurityException if the access control context does not have the
- * <code>CONFIGURE</code> permission.
+ * @param name The bundle location to check for permission. If
+ * <code>null</code> assumes <code>*</code>.
+ * @param permission The actual permission to check. This must be one
+ * of the constants defined in the
+ * <code>ConfigurationPermission</code> class.
+ *
+ * @throws SecurityException if the access control context does not
+ * have the appropriate permission
*/
- void checkPermission()
+ void checkPermission( String name )
{
// the caller's permission must be checked
final SecurityManager sm = System.getSecurityManager();
if ( sm != null )
{
- sm.checkPermission( new ConfigurationPermission( "*", ConfigurationPermission.CONFIGURE ) );
+ if (name == null) {
+ name = "*";
+ }
+
+ sm.checkPermission( new ConfigurationPermission( name, ConfigurationPermission.CONFIGURE ) );
}
}
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationBase.java b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationBase.java
index 3f4e839..1d9a8b4 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationBase.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationBase.java
@@ -22,7 +22,6 @@
import java.io.IOException;
import java.util.Dictionary;
import org.apache.felix.cm.PersistenceManager;
-import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;
@@ -41,30 +40,8 @@
// the basic ID of this instance
private final String baseId;
- /**
- * The statically bound bundle location, which is set explicitly by calling
- * the Configuration.setBundleLocation(String) method.
- */
- private volatile String staticBundleLocation;
-
- /**
- * The bundle location from dynamic binding. This value is set as the
- * configuration or factory is assigned to a ManagedService[Factory].
- */
- private volatile String dynamicBundleLocation;
-
- /**
- * The <code>ServiceReference</code> of the serviceReference which first
- * asked for this instance. This field is <code>null</code> if the instance
- * has not been handed to a serviceReference by way of the
- * <code>ManagedService.update(Dictionary)</code> or
- * <code>ManagedServiceFactory.updated(String, Dictionary)</code> method.
- */
- private volatile ServiceReference serviceReference;
-
-
protected ConfigurationBase( final ConfigurationManager configurationManager,
- final PersistenceManager persistenceManager, final String baseId, final String bundleLocation )
+ final PersistenceManager persistenceManager, final String baseId )
{
if ( configurationManager == null )
{
@@ -79,10 +56,6 @@
this.configurationManager = configurationManager;
this.persistenceManager = persistenceManager;
this.baseId = baseId;
-
- // set bundle location from persistence and/or check for dynamic binding
- this.staticBundleLocation = bundleLocation;
- this.dynamicBundleLocation = configurationManager.getDynamicBundleLocation( baseId );
}
@@ -104,168 +77,6 @@
}
- /**
- * Returns the "official" bundle location as visible from the outside
- * world of code calling into the Configuration.getBundleLocation() method.
- * <p>
- * In other words: The {@link #getStaticBundleLocation()} is returned if
- * not <code>null</code>. Otherwise the {@link #getDynamicBundleLocation()}
- * is returned (which may also be <code>null</code>).
- */
- String getBundleLocation()
- {
- if ( staticBundleLocation != null )
- {
- return staticBundleLocation;
- }
-
- return dynamicBundleLocation;
- }
-
-
- /**
- * Returns the bundle location according to actual configuration binding.
- * <p>
- * This may be different from the {@link #getBundleLocation()} result if
- * the <code>Configuration.setBundleLocation(String)</code> method has been
- * called after the configuration has been dynamically bound to a bundle.
- * <p>
- * In other words: The {@link #getDynamicBundleLocation()} is returned if
- * not <code>null</code>. Otherwise the {@link #getStaticBundleLocation()}
- * is returned (which may also be <code>null</code>).
- */
- String getBoundBundleLocation()
- {
- if ( dynamicBundleLocation != null )
- {
- return dynamicBundleLocation;
- }
-
- return staticBundleLocation;
- }
-
-
- String getDynamicBundleLocation()
- {
- return dynamicBundleLocation;
- }
-
-
- String getStaticBundleLocation()
- {
- return staticBundleLocation;
- }
-
-
- void setServiceReference( ServiceReference serviceReference )
- {
- this.serviceReference = serviceReference;
- }
-
-
- ServiceReference getServiceReference()
- {
- return serviceReference;
- }
-
-
- void setStaticBundleLocation( final String bundleLocation )
- {
- // 104.15.2.8 The bundle location will be set persistently
- this.staticBundleLocation = bundleLocation;
- storeSilently();
-
- // FELIX-1488: If a configuration is bound to a location and a new
- // location is statically set, the old binding must be removed
- // by removing the configuration from the targets and the new binding
- // must be setup by updating the configuration for new targets
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the static bundle location.
- * The following code is therefore disabled for now
- if ( ( this instanceof ConfigurationImpl ) && ( bundleLocation != null ) )
- {
- // remove configuration from current managed service [factory]
- if ( getDynamicBundleLocation() != null && !bundleLocation.equals( getDynamicBundleLocation() ) )
- {
- getConfigurationManager().revokeConfiguration( ( ConfigurationImpl ) this );
- }
-
- // check whether we have to assign the configuration to new targets
- getConfigurationManager().reassignConfiguration( ( ConfigurationImpl ) this );
- }
- *
- */
-
- // corrollary to not reassigning configurations: we have to check
- // whether the dynamic bundle location is different from the static
- // now. If so, the dynamic bundle location has to be removed.
- // also set the service reference to null because we assume to not
- // be bound to a service anymore
- if ( bundleLocation != null && getDynamicBundleLocation() != null
- && !bundleLocation.equals( getDynamicBundleLocation() ) )
- {
- setDynamicBundleLocation( null );
- setServiceReference( null );
- }
- }
-
-
- void setDynamicBundleLocation( final String bundleLocation )
- {
- this.dynamicBundleLocation = bundleLocation;
- this.configurationManager.setDynamicBundleLocation( this.getBaseId(), bundleLocation );
-
- // FELIX-1488: If a dynamically bound configuration is unbound and not
- // statically bound, it may be rebound to another bundle asking for it
- // (unless the dynamic unbind happens due to configuration deletion)
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the static bundle location.
- * The following code is therefore disabled for now
- if ( bundleLocation == null && getStaticBundleLocation() == null && ( this instanceof ConfigurationImpl ) )
- {
- getConfigurationManager().reassignConfiguration( ( ConfigurationImpl ) this );
- }
- *
- */
- }
-
-
- /**
- * Tries to bind this configuration or factory to the given bundle location:
- * <ul>
- * <li>If already dynamically bound, <code>true</code> is returned if the
- * dynamic binding equals the desired binding. Otherwise <code>false</code>
- * is returned.</li>
- * <li>If not dynamically bound but statically bound and the static binding
- * is not equal to the desired binding, <code>false</code> is returned.</li>
- * <li>Otherwise this configuration or factory is dynamically bound to the
- * desired location and <code>true</code> is returned.</li>
- * </ul>
- *
- * @param bundleLocation
- * The desired bundle location to which this configuration or
- * factory should be dynamically bound.
- * @return <code>true</code> if this configuration or factory is dynamically
- * bound to the desired location.
- */
- boolean tryBindLocation( final String bundleLocation )
- {
- if ( this.dynamicBundleLocation != null )
- {
- return this.dynamicBundleLocation.equals( bundleLocation );
- }
- else if ( this.staticBundleLocation != null && !this.staticBundleLocation.equals( bundleLocation ) )
- {
- return false;
- }
-
- setDynamicBundleLocation( bundleLocation );
- return true;
- }
-
-
abstract void store() throws IOException;
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java
index 2ff0b4d..676c33a 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java
@@ -115,12 +115,25 @@
private static final String CONFIGURATION_NEW = "_felix_.cm.newConfiguration";
/**
- * The factory serviceReference PID of this configuration or <code>null</code> if this
+ * The factory PID of this configuration or <code>null</code> if this
* is not a factory configuration.
*/
private final String factoryPID;
/**
+ * The statically bound bundle location, which is set explicitly by calling
+ * the Configuration.setBundleLocation(String) method or when the
+ * configuration was created with the two-argument method.
+ */
+ private volatile String staticBundleLocation;
+
+ /**
+ * The bundle location from dynamic binding. This value is set as the
+ * configuration or factory is assigned to a ManagedService[Factory].
+ */
+ private volatile String dynamicBundleLocation;
+
+ /**
* The configuration data of this configuration instance. This is a private
* copy of the properties of which a copy is made when the
* {@link #getProperties()} method is called. This field is
@@ -159,13 +172,16 @@
ConfigurationImpl( ConfigurationManager configurationManager, PersistenceManager persistenceManager,
Dictionary properties )
{
- super( configurationManager, persistenceManager, ( String ) properties.remove( Constants.SERVICE_PID ),
- ( String ) properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION ) );
+ super( configurationManager, persistenceManager, ( String ) properties.remove( Constants.SERVICE_PID ) );
this.factoryPID = ( String ) properties.remove( ConfigurationAdmin.SERVICE_FACTORYPID );
this.isDeleted = false;
this.lastUpdatedTime = -1;
+ // set bundle location from persistence and/or check for dynamic binding
+ this.staticBundleLocation = ( String ) properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION ) ;
+ this.dynamicBundleLocation = configurationManager.getDynamicBundleLocation( getBaseId() );
+
// set the properties internally
configureFromPersistence( properties );
}
@@ -174,12 +190,16 @@
ConfigurationImpl( ConfigurationManager configurationManager, PersistenceManager persistenceManager, String pid,
String factoryPid, String bundleLocation ) throws IOException
{
- super( configurationManager, persistenceManager, pid, bundleLocation );
+ super( configurationManager, persistenceManager, pid );
this.factoryPID = factoryPid;
this.isDeleted = false;
this.lastUpdatedTime = -1;
+ // set bundle location from persistence and/or check for dynamic binding
+ this.staticBundleLocation = bundleLocation;
+ this.dynamicBundleLocation = configurationManager.getDynamicBundleLocation( getBaseId() );
+
// first "update"
this.properties = null;
setLastModificationTime();
@@ -218,6 +238,96 @@
}
+
+
+ /**
+ * Returns the "official" bundle location as visible from the outside
+ * world of code calling into the Configuration.getBundleLocation() method.
+ * <p>
+ * In other words: The {@link #getStaticBundleLocation()} is returned if
+ * not <code>null</code>. Otherwise the {@link #getDynamicBundleLocation()}
+ * is returned (which may also be <code>null</code>).
+ */
+ String getBundleLocation()
+ {
+ if ( staticBundleLocation != null )
+ {
+ return staticBundleLocation;
+ }
+
+ return dynamicBundleLocation;
+ }
+
+
+ String getDynamicBundleLocation()
+ {
+ return dynamicBundleLocation;
+ }
+
+
+ String getStaticBundleLocation()
+ {
+ return staticBundleLocation;
+ }
+
+
+ void setStaticBundleLocation( final String bundleLocation )
+ {
+ // CM 1.4; needed for bundle location change at the end
+ final String oldBundleLocation = getBundleLocation();
+
+ // 104.15.2.8 The bundle location will be set persistently
+ this.staticBundleLocation = bundleLocation;
+ storeSilently();
+
+ // check whether the dynamic bundle location is different from the
+ // static now. If so, the dynamic bundle location has to be
+ // removed.
+ if ( bundleLocation != null && getDynamicBundleLocation() != null
+ && !bundleLocation.equals( getDynamicBundleLocation() ) )
+ {
+ setDynamicBundleLocation( null, false );
+ }
+
+ // CM 1.4
+ this.getConfigurationManager().locationChanged( this, oldBundleLocation );
+ }
+
+
+ void setDynamicBundleLocation( final String bundleLocation, final boolean dispatchConfiguration )
+ {
+ // CM 1.4; needed for bundle location change at the end
+ final String oldBundleLocation = getBundleLocation();
+
+ this.dynamicBundleLocation = bundleLocation;
+ this.getConfigurationManager().setDynamicBundleLocation( this.getBaseId(), bundleLocation );
+
+ // CM 1.4
+ if ( dispatchConfiguration )
+ {
+ this.getConfigurationManager().locationChanged( this, oldBundleLocation );
+
+ }
+ }
+
+
+ /**
+ * Dynamically binds this configuration to the given location unless
+ * the configuration is already bound (statically or dynamically). In
+ * the case of this configuration to be dynamically bound a
+ * <code>CM_LOCATION_CHANGED</code> event is dispatched.
+ */
+ boolean tryBindLocation( final String bundleLocation )
+ {
+ if ( this.getBundleLocation() == null )
+ {
+ setDynamicBundleLocation( bundleLocation, true );
+ }
+
+ return true;
+ }
+
+
/**
* Returns an optionally deep copy of the properties of this configuration
* instance.
@@ -451,7 +561,10 @@
{
synchronized ( this )
{
- this.lastUpdatedTime = lastModificationTime;
+ if ( this.lastUpdatedTime < lastModificationTime )
+ {
+ this.lastUpdatedTime = lastModificationTime;
+ }
}
}
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
index b94e322..f734da1 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
@@ -95,9 +95,6 @@
// configurations
private static Random numberGenerator;
- // comparator used to keep the ordered persistence manager map
- private static final Comparator cmRankComp = new RankingComparator( true, ConfigurationPlugin.CM_RANKING );
-
// the BundleContext of the Configuration Admin Service bundle
private BundleContext bundleContext;
@@ -418,45 +415,16 @@
}
- // ---------- ConfigurationAdminImpl support
-
- /*
- * (non-Javadoc)
- *
- * @see org.osgi.service.cm.ConfigurationAdmin#createFactoryConfiguration(java.lang.String)
- */
ConfigurationImpl createFactoryConfiguration( ConfigurationAdminImpl configurationAdmin, String factoryPid )
throws IOException
{
- // check Persmission if factory is bound to another bundle
- Factory factory = getFactory( factoryPid );
- if ( factory.getBundleLocation() != null
- && !factory.getBundleLocation().equals( configurationAdmin.getBundle().getLocation() ) )
- {
- configurationAdmin.checkPermission();
- }
-
- // create the configuration
- String pid = createPid( factoryPid );
- ConfigurationImpl config = createConfiguration( pid, factoryPid, configurationAdmin.getBundle().getLocation() );
-
- return config;
+ return createFactoryConfiguration( factoryPid, configurationAdmin.getBundle().getLocation() );
}
- /*
- * (non-Javadoc)
- *
- * @see org.osgi.service.cm.ConfigurationAdmin#createFactoryConfiguration(java.lang.String,
- * java.lang.String)
- */
ConfigurationImpl createFactoryConfiguration( String factoryPid, String location ) throws IOException
{
- // create the configuration
- String pid = createPid( factoryPid );
- ConfigurationImpl config = createConfiguration( pid, factoryPid, location );
-
- return config;
+ return createConfiguration( createPid( factoryPid ), factoryPid, location );
}
@@ -509,9 +477,6 @@
filter = bundleContext.createFilter( filterString );
}
- boolean unprivileged = configurationAdmin != null && !configurationAdmin.hasPermission();
- String location = unprivileged ? configurationAdmin.getBundle().getLocation() : null;
-
List configList = new ArrayList();
PersistenceManager[] pmList = getPersistenceManagers();
@@ -529,14 +494,11 @@
continue;
}
- // ignore this config if not privileged and not bound to bundle
- if ( unprivileged )
+ // CM 1.4 / 104.13.2.3 Permission required
+ if ( !configurationAdmin.hasPermission( ( String ) config
+ .get( ConfigurationAdmin.SERVICE_BUNDLELOCATION ) ) )
{
- Object boundLocation = config.get( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
- if ( !location.equals( boundLocation ) )
- {
- continue;
- }
+ continue;
}
// check filter
@@ -590,6 +552,16 @@
}
+ void locationChanged( ConfigurationImpl config, String oldLocation )
+ {
+ fireConfigurationEvent( ConfigurationEvent.CM_LOCATION_CHANGED, config.getPid(), config.getFactoryPid() );
+ if ( oldLocation != null )
+ {
+ updateThread.schedule( new LocationChanged( config, oldLocation ) );
+ }
+ }
+
+
void fireConfigurationEvent( int type, String pid, String factoryPid )
{
FireConfigurationEvent event = new FireConfigurationEvent( type, pid, factoryPid );
@@ -620,19 +592,7 @@
final ConfigurationImpl cfg = configs[i];
if ( location.equals( cfg.getDynamicBundleLocation() ) )
{
- cfg.setDynamicBundleLocation( null );
- }
- }
-
- // we only reset dynamic bindings, which are only present in
- // cached factories, hence only consider cached factories here
- final Factory[] factories = getCachedFactories();
- for ( int i = 0; i < factories.length; i++ )
- {
- final Factory factory = factories[i];
- if ( location.equals( factory.getDynamicBundleLocation() ) )
- {
- factory.setDynamicBundleLocation( null );
+ cfg.setDynamicBundleLocation( null, true );
}
}
}
@@ -658,18 +618,15 @@
else
{
// sort the references according to the cmRanking property
- SortedSet pms = new TreeSet( new RankingComparator( false ) );
- for ( int i = 0; i < refs.length; i++ )
+ if ( refs.length > 1 )
{
- pms.add( refs[i] );
+ Arrays.sort( refs, RankingComparator.SRV_RANKING );
}
// create the service array from the sorted set of referenecs
- int pmIndex = 0;
- for ( Iterator pi = pms.iterator(); pi.hasNext(); pmIndex++ )
+ for ( int i = 0; i < refs.length; i++ )
{
- ServiceReference ref = ( ServiceReference ) pi.next();
- Object service = persistenceManagerTracker.getService( ref );
+ Object service = persistenceManagerTracker.getService( refs[i] );
if ( service != null )
{
pmList.add( new CachingPersistenceManagerProxy( ( PersistenceManager ) service ) );
@@ -832,32 +789,34 @@
}
// sort the plugins by their service.cmRanking
- SortedSet pluginSet = new TreeSet( cmRankComp );
- for ( int i = 0; i < plugins.length; i++ )
+ if ( plugins.length > 1 )
{
- pluginSet.add( plugins[i] );
+ Arrays.sort( plugins, RankingComparator.CM_RANKING );
}
// call the plugins in order
- for ( Iterator pi = pluginSet.iterator(); pi.hasNext(); )
+ for ( int i = 0; i < plugins.length; i++ )
{
- ServiceReference pluginRef = ( ServiceReference ) pi.next();
+ ServiceReference pluginRef = plugins[i];
ConfigurationPlugin plugin = ( ConfigurationPlugin ) bundleContext.getService( pluginRef );
- try
+ if ( plugin != null )
{
- plugin.modifyConfiguration( sr, props );
+ try
+ {
+ plugin.modifyConfiguration( sr, props );
+ }
+ catch ( Throwable t )
+ {
+ log( LogService.LOG_ERROR, "Unexpected problem calling configuration plugin "
+ + toString( pluginRef ), t );
+ }
+ finally
+ {
+ // ensure ungetting the plugin
+ bundleContext.ungetService( pluginRef );
+ }
+ cfg.setAutoProperties( props, false );
}
- catch ( Throwable t )
- {
- log( LogService.LOG_ERROR, "Unexpected problem calling configuration plugin " + toString( pluginRef ),
- t );
- }
- finally
- {
- // ensure ungetting the plugin
- bundleContext.ungetService( pluginRef );
- }
- cfg.setAutoProperties( props, false );
}
}
@@ -1056,8 +1015,225 @@
}
}
- // ---------- inner classes ------------------------------------------------
+ /**
+ * Checks whether the bundle is allowed to receive the configuration
+ * with the given location binding.
+ * <p>
+ * This method implements the logic defined CM 1.4 / 104.4.1:
+ * <ul>
+ * <li>If the location is <code>null</code> (the configuration is not
+ * bound yet), assume the bundle is allowed</li>
+ * <li>If the location is a single location (no leading "?"), require
+ * the bundle's location to match</li>
+ * <li>If the location is a multi-location (leading "?"), assume the
+ * bundle is allowed if there is no security manager. If there is a
+ * security manager, check whether the bundle has "target" permission
+ * on this location.</li>
+ * </ul>
+ */
+ boolean canReceive( final Bundle bundle, final String location )
+ {
+ if ( location == null )
+ {
+ return true;
+ }
+ else if ( location.startsWith( "?" ) )
+ {
+ // multi-location
+ if ( System.getSecurityManager() != null )
+ {
+ return bundle.hasPermission( new ConfigurationPermission( location, ConfigurationPermission.TARGET ) );
+ }
+ return true;
+ }
+ else
+ {
+ // single location, must match
+ return location.equals( bundle.getLocation() );
+ }
+ }
+
+ // ---------- inner classes
+
+ private ServiceHelper createServiceHelper( ConfigurationImpl config )
+ {
+ if ( config.getFactoryPid() == null )
+ {
+ return new ManagedServiceHelper( config );
+ }
+ return new ManagedServiceFactoryHelper( config );
+ }
+
+ private abstract class ServiceHelper
+ {
+ protected final ConfigurationImpl config;
+
+ private final Dictionary properties;
+
+ protected ServiceHelper( ConfigurationImpl config )
+ {
+ this.config = config;
+ this.properties = config.getProperties( true );
+ }
+
+ final ServiceReference[] getServices( )
+ {
+ try
+ {
+ ServiceReference[] refs = doGetServices();
+ if ( refs != null && refs.length > 1 )
+ {
+ Arrays.sort( refs, RankingComparator.SRV_RANKING );
+ }
+ return refs;
+ }
+ catch ( InvalidSyntaxException ise )
+ {
+ log( LogService.LOG_ERROR, "Service selection filter is invalid to update " + config, ise );
+ }
+ return null;
+ }
+
+
+ protected abstract ServiceReference[] doGetServices() throws InvalidSyntaxException;
+
+
+ abstract void provide( ServiceReference service );
+
+
+ abstract void remove( ServiceReference service );
+
+
+ protected Dictionary getProperties( String targetPid, ServiceReference service )
+ {
+ Dictionary props = new CaseInsensitiveDictionary( this.properties );
+ callPlugins( props, targetPid, service, config );
+ return props;
+ }
+ }
+
+ private class ManagedServiceHelper extends ServiceHelper
+ {
+
+ protected ManagedServiceHelper( ConfigurationImpl config )
+ {
+ super( config );
+ }
+
+
+ public ServiceReference[] doGetServices() throws InvalidSyntaxException
+ {
+ return bundleContext.getServiceReferences( ManagedService.class.getName(), "(" + Constants.SERVICE_PID
+ + "=" + config.getPid() + ")" );
+ }
+
+
+ public void provide( ServiceReference service )
+ {
+ ManagedService srv = ( ManagedService ) bundleContext.getService( service );
+ if ( srv != null )
+ {
+ try
+ {
+ Dictionary props = getProperties( this.config.getPid(), service );
+ srv.updated( props );
+ }
+ catch ( Throwable t )
+ {
+ handleCallBackError( t, service, config );
+ }
+ finally
+ {
+ bundleContext.ungetService( service );
+ }
+ }
+ }
+
+
+ public void remove( ServiceReference service )
+ {
+ ManagedService srv = ( ManagedService ) bundleContext.getService( service );
+ try
+ {
+ srv.updated( null );
+ }
+ catch ( Throwable t )
+ {
+ handleCallBackError( t, service, config );
+ }
+ finally
+ {
+ bundleContext.ungetService( service );
+ }
+ }
+
+ }
+
+ private class ManagedServiceFactoryHelper extends ServiceHelper
+ {
+
+ protected ManagedServiceFactoryHelper( ConfigurationImpl config )
+ {
+ super( config );
+ // TODO Auto-generated constructor stub
+ }
+
+
+ public ServiceReference[] doGetServices() throws InvalidSyntaxException
+ {
+ return bundleContext.getServiceReferences( ManagedServiceFactory.class.getName(), "("
+ + Constants.SERVICE_PID + "=" + config.getFactoryPid() + ")" );
+ }
+
+
+ public void provide( ServiceReference service )
+ {
+ ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext.getService( service );
+ if ( srv != null )
+ {
+ try
+ {
+ Dictionary props = getProperties( this.config.getFactoryPid(), service );
+ srv.updated( config.getPid(), props );
+ }
+ catch ( Throwable t )
+ {
+ handleCallBackError( t, service, config );
+ }
+ finally
+ {
+ bundleContext.ungetService( service );
+ }
+ }
+ }
+
+
+ public void remove( ServiceReference service )
+ {
+ ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext.getService( service );
+ try
+ {
+ srv.deleted( config.getPid() );
+ }
+ catch ( Throwable t )
+ {
+ handleCallBackError( t, service, config );
+ }
+ finally
+ {
+ bundleContext.ungetService( service );
+ }
+ }
+
+ }
+
+ /**
+ * The <code>ManagedServiceUpdate</code> updates a freshly registered
+ * <code>ManagedService</code> with a specific configuration. If a
+ * ManagedService is registered with multiple PIDs an instance of this
+ * class is used for each registered PID.
+ */
private class ManagedServiceUpdate implements Runnable
{
private final String pid;
@@ -1110,8 +1286,7 @@
// only update configuration if lastModificationTime is less than
// lastUpdateTime
Dictionary properties = rawProperties;
- if ( properties != null && config != null && lastModificationTime <= config.getLastUpdatedTime()
- && sr.equals( config.getServiceReference() ) )
+ if ( properties != null && config != null && lastModificationTime < config.getLastUpdatedTime() )
{
if ( isLogEnabled( LogService.LOG_DEBUG ) )
{
@@ -1127,7 +1302,7 @@
{
if ( isLogEnabled( LogService.LOG_DEBUG ) )
{
- log( LogService.LOG_DEBUG, "Updating configuration " + config.getPid() + " to modification #"
+ log( LogService.LOG_DEBUG, "Updating configuration " + pid + " to modification #"
+ config.getLastModificationTime(), null );
}
@@ -1142,32 +1317,17 @@
return;
}
- // 104.3 Ignore duplicate PIDs from other bundles and report
- // them to the log
- // 104.4.1 No update call back for PID already bound to another
- // bundle location
- // 104.4.1 assign configuration to bundle if unassigned
- if ( !config.tryBindLocation( serviceBundle.getLocation() ) )
+ // CM 1.4 / 104.13.2.2
+ if ( !canReceive( serviceBundle, config.getBundleLocation() ) )
{
- log( LogService.LOG_ERROR, "Cannot use configuration " + pid + " for "
- + ConfigurationManager.toString( sr ) + ": Configuration bound to bundle "
- + config.getBundleLocation(), null );
+ log( LogService.LOG_ERROR,
+ "Cannot use configuration " + pid + " for " + ConfigurationManager.toString( sr )
+ + ": No visibility to configuration bound to " + config.getBundleLocation(), null );
return;
}
- // 104.3 Report an error in the log if more than one service
- // with the same PID asks for the configuration
- if ( config.getServiceReference() == null )
- {
- // assign the configuration to the service
- config.setServiceReference( sr );
- }
- else if ( !sr.equals( config.getServiceReference() ) )
- {
- log( LogService.LOG_ERROR, "Configuration for " + pid + " has already been used for service "
- + ConfigurationManager.toString( config.getServiceReference() )
- + " and will now also be given to " + ConfigurationManager.toString( sr ), null );
- }
+ // 104.4.2 Dynamic Binding
+ config.tryBindLocation( serviceBundle.getLocation() );
// prepare the configuration for the service (call plugins)
callPlugins( properties, pid, sr, config );
@@ -1207,6 +1367,13 @@
}
}
+ /**
+ * The <code>ManagedServiceFactoryUpdate</code> updates a freshly
+ * registered <code>ManagedServiceFactory</code> with a specific
+ * configuration. If a ManagedServiceFactory is registered with
+ * multiple PIDs an instance of this class is used for each registered
+ * PID.
+ */
private class ManagedServiceFactoryUpdate implements Runnable
{
private final String factoryPid;
@@ -1215,8 +1382,6 @@
private final ManagedServiceFactory service;
- private final Factory factory;
-
private final Map configs;
private final Map stamps;
@@ -1293,7 +1458,6 @@
log( LogService.LOG_ERROR, "Cannot get factory mapping for factory PID " + factoryPid, ioe );
}
- this.factory = factory;
this.configs = configs;
this.stamps = stamps;
}
@@ -1312,17 +1476,6 @@
return;
}
- final String serviceBundleLocation = serviceBundle.getLocation();
- if ( !factory.tryBindLocation( serviceBundleLocation ) )
- {
- // factory PID is bound to another bundle
- log( LogService.LOG_ERROR, "Cannot use factory configuration " + factoryPid + " for "
- + ConfigurationManager.toString( sr ) + ": Configuration bound to bundle "
- + factory.getBundleLocation(), null );
-
- return;
- }
-
for ( Iterator ci=configs.entrySet().iterator(); ci.hasNext(); )
{
final Map.Entry entry = (Map.Entry) ci.next();
@@ -1330,7 +1483,7 @@
final Dictionary properties = (Dictionary) entry.getValue();
final long lastModificationTime = ( ( Long ) stamps.get( cfg ) ).longValue();
- if ( lastModificationTime <= cfg.getLastUpdatedTime() && sr.equals( cfg.getServiceReference() ) )
+ if ( lastModificationTime <= cfg.getLastUpdatedTime() )
{
if ( isLogEnabled( LogService.LOG_DEBUG ) )
{
@@ -1338,7 +1491,7 @@
+ cfg.getLastModificationTime() + " has already been updated to update #"
+ cfg.getLastUpdatedTime() + ", nothing to be done anymore.", null );
}
- return;
+ continue;
}
if ( isLogEnabled( LogService.LOG_DEBUG ) )
@@ -1347,31 +1500,17 @@
+ cfg.getLastModificationTime(), null );
}
- // check bundle location of configuration
- if ( !cfg.tryBindLocation( serviceBundleLocation ) )
+ // CM 1.4 / 104.13.2.1
+ if ( !canReceive( serviceBundle, cfg.getBundleLocation() ) )
{
- // configuration is bound to another bundle
- log( LogService.LOG_ERROR, "Cannot use configuration " + cfg.getPid() + " (factory " + factoryPid
- + ") for " + ConfigurationManager.toString( sr ) + ": Configuration bound to bundle "
- + cfg.getBundleLocation(), null );
-
+ log( LogService.LOG_ERROR,
+ "Cannot use configuration " + cfg.getPid() + " for " + ConfigurationManager.toString( sr )
+ + ": No visibility to configuration bound to " + cfg.getBundleLocation(), null );
continue;
}
- // 104.3 Report an error in the log if more than one service
- // with the same PID asks for the configuration
- if ( cfg.getServiceReference() == null )
- {
- // assign the configuration to the service
- cfg.setServiceReference( sr );
- }
- else if ( !sr.equals( cfg.getServiceReference() ) )
- {
- log( LogService.LOG_ERROR, "Configuration for " + cfg.getPid() + " (factory " + factoryPid
- + ") has already been used for service "
- + ConfigurationManager.toString( cfg.getServiceReference() )
- + " and will now also be given to " + ConfigurationManager.toString( sr ), null );
- }
+ // 104.4.2 Dynamic Binding
+ cfg.tryBindLocation( serviceBundle.getLocation() );
// prepare the configuration for the service (call plugins)
// call the plugins with cm.target set to the service's factory PID
@@ -1414,11 +1553,18 @@
}
}
+
+ /**
+ * The <code>UpdateConfiguration</code> is used to update
+ * <code>ManagedService[Factory]</code> services with the configuration
+ * they are subscribed to. This may cause the configuration to be
+ * supplied to multiple services.
+ */
private class UpdateConfiguration implements Runnable
{
private final ConfigurationImpl config;
- private final Dictionary properties;
+ private final ServiceHelper helper;
private final long lastModificationTime;
@@ -1427,13 +1573,7 @@
this.config = config;
synchronized ( config )
{
- Dictionary props = config.getProperties( true );
- if ( props == null )
- {
- props = new Hashtable();
- }
-
- this.properties = props;
+ this.helper = createServiceHelper( config );
this.lastModificationTime = config.getLastModificationTime();
}
}
@@ -1441,227 +1581,62 @@
public void run()
{
- try
+ if ( lastModificationTime <= config.getLastUpdatedTime() )
{
- // only update configuration if lastModificationTime is
- // less than lastUpdateTime
- // (this must be inside the try-catch-finally to ensure
- // the event is sent regardless of ManagedService[Factory]
- // update)
- if ( lastModificationTime <= config.getLastUpdatedTime() )
- {
- if ( isLogEnabled( LogService.LOG_DEBUG ) )
- {
- log( LogService.LOG_DEBUG, "Configuration " + config.getPid() + " at modification #"
- + config.getLastModificationTime() + " has already been updated to update #"
- + config.getLastUpdatedTime() + ", nothing to be done anymore.", null );
- }
- return;
- }
-
if ( isLogEnabled( LogService.LOG_DEBUG ) )
{
- log( LogService.LOG_DEBUG, "Updating configuration " + config.getPid() + " to modification #"
+ log( LogService.LOG_DEBUG,
+ "Configuration " + config.getPid() + " at modification #" + config.getLastModificationTime()
+ + " has already been updated to update #" + config.getLastUpdatedTime()
+ + ", nothing to be done anymore.", null );
+ }
+ return;
+ }
+
+ if ( isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ log(
+ LogService.LOG_DEBUG,
+ "Updating configuration " + config.getPid() + " to modification #"
+ config.getLastModificationTime(), null );
- }
-
- if ( config.getFactoryPid() == null )
- {
- final ServiceReference[] srList = bundleContext.getServiceReferences( ManagedService.class
- .getName(), "(" + Constants.SERVICE_PID + "=" + config.getPid() + ")" );
- if ( srList != null )
- {
- // find the primary configuration owner
- final ServiceReference ownerRef = getOwner( config, srList );
- final String bundleLocation = ( ownerRef != null ) ? ownerRef.getBundle().getLocation()
- : config.getBoundBundleLocation();
-
- // if the configuration is unbound, bind to owner
- if ( config.getBundleLocation() == null )
- {
- config.setDynamicBundleLocation( bundleLocation );
- }
- final String configBundleLocation = config.getBundleLocation();
-
- // provide configuration to all services from the
- // correct bundle
- for ( int i = 0; i < srList.length; i++ )
- {
-
- final ServiceReference userRef = srList[i];
- final String userRefLocation = userRef.getBundle().getLocation();
-
- // only consider the entry if in the same bundle
- if ( !userRefLocation.equals( configBundleLocation ) )
- {
- log( LogService.LOG_ERROR, "Cannot use configuration " + config.getPid() + " for "
- + ConfigurationManager.toString( userRef ) + ": Configuration bound to bundle "
- + configBundleLocation, null );
-
- continue;
- }
-
- // 104.3 Report an error in the log if more than one
- // service with the same PID asks for the
- // configuration
- if ( userRef != ownerRef )
- {
- log( LogService.LOG_ERROR, "Configuration for " + config.getPid()
- + " has already been used for service " + ConfigurationManager.toString( ownerRef )
- + " and will now also be given to " + ConfigurationManager.toString( userRef ), null );
- }
-
- try
- {
- final ManagedService srv = ( ManagedService ) bundleContext.getService( userRef );
- if ( srv != null )
- {
- Dictionary props = new CaseInsensitiveDictionary( properties );
- callPlugins( props, config.getPid(), userRef, config );
- srv.updated( props );
- }
- }
- catch ( Throwable t )
- {
- handleCallBackError( t, userRef, config );
- }
- finally
- {
- bundleContext.ungetService( userRef );
- }
-
- // update the lastUpdatedTime
- config.setLastUpdatedTime( lastModificationTime );
-
- if ( isLogEnabled( LogService.LOG_DEBUG ) )
- {
- log( LogService.LOG_DEBUG, "Updated configuration " + config.getPid() + " to update #"
- + config.getLastUpdatedTime(), null );
- }
- }
- }
- }
- else
- {
- ServiceReference[] srList = bundleContext.getServiceReferences( ManagedServiceFactory.class
- .getName(), "(" + Constants.SERVICE_PID + "=" + config.getFactoryPid() + ")" );
- if ( srList != null && srList.length > 0 )
- {
- // find the primary configuration owner
- final ServiceReference ownerRef = getOwner( config, srList );
- final String bundleLocation = ( ownerRef != null ) ? ownerRef.getBundle().getLocation()
- : config.getBoundBundleLocation();
-
- // if the configuration is unbound, bind to owner
- if ( config.getBundleLocation() == null )
- {
- config.setDynamicBundleLocation( bundleLocation );
- }
- final String configBundleLocation = config.getBundleLocation();
-
- // provide configuration to all services from the
- // correct bundle
- for ( int i = 0; i < srList.length; i++ )
- {
- final ServiceReference ref = srList[i];
- final String refLocation = ref.getBundle().getLocation();
-
- // only consider the entry if in the same bundle
- if ( !refLocation.equals( configBundleLocation ) )
- {
- log( LogService.LOG_ERROR, "Cannot use configuration " + config.getPid() + " (factory "
- + config.getFactoryPid() + ") for " + ConfigurationManager.toString( ref )
- + ": Configuration bound to bundle " + configBundleLocation, null );
-
- continue;
- }
-
- // 104.3 Report an error in the log if more than one
- // service with the same PID asks for the
- // configuration
- if ( ref != ownerRef )
- {
- log( LogService.LOG_ERROR, "Configuration for " + config.getPid() + " (factory "
- + config.getFactoryPid() + ") has already been used for service "
- + ConfigurationManager.toString( ownerRef ) + " and will now also be given to "
- + ConfigurationManager.toString( ref ), null );
- }
-
- try
- {
- final ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext
- .getService( ref );
- if ( srv != null )
- {
- Dictionary props = new CaseInsensitiveDictionary( properties );
- callPlugins( props, config.getFactoryPid(), ref, config );
- srv.updated( config.getPid(), props );
- }
- }
- catch ( Throwable t )
- {
- handleCallBackError( t, ref, config );
- }
- finally
- {
- bundleContext.ungetService( ref );
- }
-
- // update the lastUpdatedTime
- config.setLastUpdatedTime( lastModificationTime );
-
- if ( isLogEnabled( LogService.LOG_DEBUG ) )
- {
- log( LogService.LOG_DEBUG, "Updated configuration " + config.getPid() + " to update #"
- + config.getLastUpdatedTime(), null );
- }
- }
- }
- }
}
- catch ( InvalidSyntaxException ise )
- {
- log( LogService.LOG_ERROR, "Service selection filter is invalid to update " + config, ise );
- }
- }
-
- private ServiceReference getOwner( ConfigurationImpl config, ServiceReference[] srList )
- {
- // find the current owner among the references (if any)
- if ( config.getServiceReference() != null )
+ final ServiceReference[] srList = helper.getServices();
+ if ( srList != null )
{
+ // optionally bind dynamically to the first service
+ config.tryBindLocation( srList[0].getBundle().getLocation() );
+
+ final String configBundleLocation = config.getBundleLocation();
+
+ // provide configuration to all services from the
+ // correct bundle
for ( int i = 0; i < srList.length; i++ )
{
- if ( srList[i].equals( config.getServiceReference() ) )
+ final ServiceReference ref = srList[i];
+
+ // CM 1.4 / 104.13.2.2
+ if ( !canReceive( ref.getBundle(), configBundleLocation ) )
{
- return srList[i];
+ log( LogService.LOG_ERROR, "Cannot use configuration " + config.getPid() + " for "
+ + ConfigurationManager.toString( ref ) + ": No visibility to configuration bound to "
+ + configBundleLocation, null );
+ continue;
+ }
+
+ helper.provide( ref );
+
+ if ( isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ log( LogService.LOG_DEBUG,
+ "Updated configuration " + config.getPid() + " to update #" + config.getLastUpdatedTime(),
+ null );
}
}
- }
- // configuration has never been supplied or the binding is stale
- // if the configuration is location bound, find a service reference
- // from the same bundle
- final String configLocation = config.getBoundBundleLocation();
- if (configLocation != null) {
- for ( int i = 0; i < srList.length; i++ )
- {
- if ( configLocation.equals(srList[i].getBundle().getLocation() ) )
- {
- return srList[i];
- }
- }
- // no service from the same bundle found, thus we cannot
- // find a new owner !!
- return null;
+ // update the lastUpdatedTime
+ config.setLastUpdatedTime( lastModificationTime );
}
- // configuration is not location bound (yet)
-
- // just use the first entry in the list as the new owner
- final ServiceReference ownerRef = srList[0];
- config.setServiceReference( ownerRef );
- return ownerRef;
}
@@ -1671,6 +1646,12 @@
}
}
+
+ /**
+ * The <code>DeleteConfiguration</code> class is used to inform
+ * <code>ManagedService[Factory]</code> services of a configuration
+ * being deleted.
+ */
private class DeleteConfiguration implements Runnable
{
@@ -1686,7 +1667,7 @@
* final and cannot be reset.
*/
this.config = config;
- this.configLocation = config.getBoundBundleLocation();
+ this.configLocation = config.getBundleLocation();
}
@@ -1694,84 +1675,34 @@
{
final String pid = config.getPid();
final String factoryPid = config.getFactoryPid();
+ final ServiceHelper helper = createServiceHelper( config );
- try
+ ServiceReference[] srList = helper.getServices( );
+ if ( srList != null )
{
- if ( factoryPid == null )
+ for ( int i = 0; i < srList.length; i++ )
{
- ServiceReference[] srList = bundleContext.getServiceReferences( ManagedService.class.getName(), "("
- + Constants.SERVICE_PID + "=" + pid + ")" );
- if ( srList != null )
+ final ServiceReference sr = srList[i];
+ if ( canReceive( sr.getBundle(), configLocation ) )
{
- for ( int i = 0; i < srList.length; i++ )
- {
- final ServiceReference sr = srList[i];
- if ( sr.getBundle().getLocation().equals( configLocation ) )
- {
- // only if the service is from the bound bundle
- final ManagedService srv = ( ManagedService ) bundleContext.getService( sr );
- try
- {
- srv.updated( null );
- }
- catch ( Throwable t )
- {
- handleCallBackError( t, sr, config );
- }
- finally
- {
- bundleContext.ungetService( sr );
- }
- }
- }
- }
- }
- else
- {
- // remove the pid from the factory
- try
- {
- Factory factory = getFactory( factoryPid );
- factory.removePID( pid );
- factory.store();
- }
- catch ( IOException ioe )
- {
- log( LogService.LOG_ERROR, "Failed removing " + pid + " from the factory " + factoryPid, ioe );
- }
-
- ServiceReference[] srList = bundleContext.getServiceReferences( ManagedServiceFactory.class
- .getName(), "(" + Constants.SERVICE_PID + "=" + factoryPid + ")" );
- if ( srList != null)
- {
- for ( int i = 0; i < srList.length; i++ )
- {
- final ServiceReference sr = srList[i];
- if ( sr.getBundle().getLocation().equals( configLocation ) )
- {
- // only if the service is from the bound bundle
- final ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext
- .getService( sr );
- try
- {
- srv.deleted( pid );
- }
- catch ( Throwable t )
- {
- handleCallBackError( t, sr, config );
- }
- finally
- {
- bundleContext.ungetService( sr );
- }
- }
- }
+ helper.remove( sr );
}
}
}
- catch ( InvalidSyntaxException ise )
+
+ if ( factoryPid != null )
{
- log( LogService.LOG_ERROR, "Service selection filter is invalid to update " + config, ise );
+ // remove the pid from the factory
+ try
+ {
+ Factory factory = getFactory( factoryPid );
+ factory.removePID( pid );
+ factory.store();
+ }
+ catch ( IOException ioe )
+ {
+ log( LogService.LOG_ERROR, "Failed removing " + pid + " from the factory " + factoryPid, ioe );
+ }
}
}
@@ -1781,6 +1712,56 @@
}
}
+ private class LocationChanged implements Runnable
+ {
+ private final ConfigurationImpl config;
+ private final String oldLocation;
+
+
+ LocationChanged( ConfigurationImpl config, String oldLocation )
+ {
+ this.config = config;
+ this.oldLocation = oldLocation;
+ }
+
+
+ public void run()
+ {
+ ServiceHelper helper = createServiceHelper( this.config );
+ ServiceReference[] srList = helper.getServices( );
+ if ( srList != null )
+ {
+ // make sure the config is dynamically bound to the first
+ // service if it has been unbound causing this update
+ config.tryBindLocation( srList[0].getBundle().getLocation() );
+
+ for ( int i = 0; i < srList.length; i++ )
+ {
+ final ServiceReference sr = srList[i];
+ final boolean wasVisible = canReceive( sr.getBundle(), oldLocation );
+ final boolean isVisible = canReceive( sr.getBundle(), config.getBundleLocation() );
+ if ( wasVisible && !isVisible )
+ {
+ // call deleted method
+ helper.remove( sr );
+ }
+ else if ( !wasVisible && isVisible )
+ {
+ // call updated method
+ helper.provide( sr );
+ }
+ }
+ }
+ }
+
+
+ public String toString()
+ {
+ return "Location Changed (pid=" + config.getPid() + "): " + oldLocation + " ==> "
+ + config.getBundleLocation();
+ }
+ }
+
private class FireConfigurationEvent implements Runnable
{
private final int type;
@@ -1837,6 +1818,8 @@
return "CM_DELETED";
case ConfigurationEvent.CM_UPDATED:
return "CM_UPDATED";
+ case ConfigurationEvent.CM_LOCATION_CHANGED:
+ return "CM_LOCATION_CHANGED";
default:
return "<UNKNOWN(" + type + ")>";
}
@@ -1907,26 +1890,6 @@
return serviceObject;
}
-
-
- public void removedService( ServiceReference reference, Object service )
- {
- // check whether we can take back the configuration object
- String[] pids = getServicePid( reference );
- if ( pids != null )
- {
- for ( int i = 0; i < pids.length; i++ )
- {
- ConfigurationImpl cfg = cm.getCachedConfiguration( pids[i] );
- if ( cfg != null && reference.equals( cfg.getServiceReference() ) )
- {
- cfg.setServiceReference( null );
- }
- }
- }
-
- super.removedService( reference, service );
- }
}
private static class ManagedServiceFactoryTracker extends ServiceTracker
@@ -1958,34 +1921,7 @@
return serviceObject;
}
-
-
- public void removedService( ServiceReference reference, Object service )
- {
- // check whether we can take back the configuration objects
- String[] factoryPids = getServicePid( reference );
- if ( factoryPids != null )
- {
- for ( int i = 0; i < factoryPids.length; i++ )
- {
- Factory factory = cm.getCachedFactory( factoryPids[i] );
- if ( factory != null )
- {
- for ( Iterator pi = factory.getPIDs().iterator(); pi.hasNext(); )
- {
- String pid = ( String ) pi.next();
- ConfigurationImpl cfg = cm.getCachedConfiguration( pid );
- if ( cfg != null && reference.equals( cfg.getServiceReference() ) )
- {
- cfg.setServiceReference( null );
- }
- }
- }
- }
- }
-
- super.removedService( reference, service );
- }
-
}
+
+
}
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/Factory.java b/configadmin/src/main/java/org/apache/felix/cm/impl/Factory.java
index 48589f1..5122816 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/Factory.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/Factory.java
@@ -26,7 +26,6 @@
import java.util.Set;
import org.apache.felix.cm.PersistenceManager;
-import org.osgi.service.cm.ConfigurationAdmin;
/**
@@ -66,14 +65,14 @@
Factory( ConfigurationManager configurationManager, PersistenceManager persistenceManager, String factoryPid )
{
- super(configurationManager, persistenceManager, factoryPid, null);
+ super( configurationManager, persistenceManager, factoryPid );
}
- Factory( ConfigurationManager configurationManager, PersistenceManager persistenceManager, String factoryPid, Dictionary props )
+ Factory( ConfigurationManager configurationManager, PersistenceManager persistenceManager, String factoryPid,
+ Dictionary props )
{
- super( configurationManager, persistenceManager, factoryPid, ( String ) props
- .get( ConfigurationAdmin.SERVICE_BUNDLELOCATION ) );
+ super( configurationManager, persistenceManager, factoryPid );
// set pids
String[] pidList = ( String[] ) props.get( FACTORY_PID_LIST );
@@ -115,8 +114,6 @@
{
Hashtable props = new Hashtable();
- replaceProperty( props, ConfigurationAdmin.SERVICE_BUNDLELOCATION, getStaticBundleLocation() );
-
if ( !pids.isEmpty() )
{
props.put( FACTORY_PID_LIST, pids.toArray( new String[pids.size()] ) );
diff --git a/configadmin/src/main/java/org/osgi/service/cm/ConfigurationEvent.java b/configadmin/src/main/java/org/osgi/service/cm/ConfigurationEvent.java
new file mode 100644
index 0000000..c215ad4
--- /dev/null
+++ b/configadmin/src/main/java/org/osgi/service/cm/ConfigurationEvent.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) OSGi Alliance (2004, 2011). All Rights Reserved.
+ *
+ * Licensed 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.osgi.service.cm;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * A Configuration Event.
+ *
+ * <p>
+ * {@code ConfigurationEvent} objects are delivered to all registered
+ * {@code ConfigurationListener} service objects. ConfigurationEvents must be
+ * asynchronously delivered in chronological order with respect to each
+ * listener.
+ *
+ * <p>
+ * A type code is used to identify the type of event. The following event types
+ * are defined:
+ * <ul>
+ * <li>{@link #CM_UPDATED}</li>
+ * <li>{@link #CM_DELETED}</li>
+ * <li>{@link #CM_LOCATION_CHANGED}</li>
+ * </ul>
+ * Additional event types may be defined in the future.
+ *
+ * <p>
+ * Security Considerations. {@code ConfigurationEvent} objects do not provide
+ * {@code Configuration} objects, so no sensitive configuration information is
+ * available from the event. If the listener wants to locate the
+ * {@code Configuration} object for the specified pid, it must use
+ * {@code ConfigurationAdmin}.
+ *
+ * @see ConfigurationListener
+ * @Immutable
+ * @version $Id: 1493c281dfd9387837e6399b9b815cdc8dc43453 $
+ * @since 1.2
+ */
+public class ConfigurationEvent {
+ /**
+ * A {@code Configuration} has been updated.
+ *
+ * <p>
+ * This {@code ConfigurationEvent} type that indicates that a
+ * {@code Configuration} object has been updated with new properties.
+ *
+ * An event is fired when a call to {@link Configuration#update(Dictionary)}
+ * successfully changes a configuration.
+ */
+ public static final int CM_UPDATED = 1;
+ /**
+ * A {@code Configuration} has been deleted.
+ *
+ * <p>
+ * This {@code ConfigurationEvent} type that indicates that a
+ * {@code Configuration} object has been deleted.
+ *
+ * An event is fired when a call to {@link Configuration#delete()}
+ * successfully deletes a configuration.
+ */
+ public static final int CM_DELETED = 2;
+
+ /**
+ * The location of a {@code Configuration} has been changed.
+ *
+ * <p>
+ * This {@code ConfigurationEvent} type that indicates that the location of
+ * a {@code Configuration} object has been changed.
+ *
+ * An event is fired when a call to
+ * {@link Configuration#setBundleLocation(String)} successfully changes the
+ * location.
+ *
+ * @since 1.4
+ */
+ public static final int CM_LOCATION_CHANGED = 3;
+ /**
+ * Type of this event.
+ *
+ * @see #getType()
+ */
+ private final int type;
+ /**
+ * The factory pid associated with this event.
+ */
+ private final String factoryPid;
+ /**
+ * The pid associated with this event.
+ */
+ private final String pid;
+ /**
+ * The ConfigurationAdmin service which created this event.
+ */
+ private final ServiceReference reference;
+
+ /**
+ * Constructs a {@code ConfigurationEvent} object from the given
+ * {@code ServiceReference} object, event type, and pids.
+ *
+ * @param reference The {@code ServiceReference} object of the Configuration
+ * Admin service that created this event.
+ * @param type The event type. See {@link #getType()}.
+ * @param factoryPid The factory pid of the associated configuration if the
+ * target of the configuration is a ManagedServiceFactory. Otherwise
+ * {@code null} if the target of the configuration is a
+ * ManagedService.
+ * @param pid The pid of the associated configuration.
+ */
+ public ConfigurationEvent(ServiceReference reference,
+ int type,
+ String factoryPid, String pid) {
+ this.reference = reference;
+ this.type = type;
+ this.factoryPid = factoryPid;
+ this.pid = pid;
+ if ((reference == null) || (pid == null)) {
+ throw new NullPointerException("reference and pid must not be null");
+ }
+ }
+
+ /**
+ * Returns the factory pid of the associated configuration.
+ *
+ * @return Returns the factory pid of the associated configuration if the
+ * target of the configuration is a ManagedServiceFactory. Otherwise
+ * {@code null} if the target of the configuration is a
+ * ManagedService.
+ */
+ public String getFactoryPid() {
+ return factoryPid;
+ }
+
+ /**
+ * Returns the pid of the associated configuration.
+ *
+ * @return Returns the pid of the associated configuration.
+ */
+ public String getPid() {
+ return pid;
+ }
+
+ /**
+ * Return the type of this event.
+ * <p>
+ * The type values are:
+ * <ul>
+ * <li>{@link #CM_UPDATED}</li>
+ * <li>{@link #CM_DELETED}</li>
+ * <li>{@link #CM_LOCATION_CHANGED}</li>
+ * </ul>
+ *
+ * @return The type of this event.
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * Return the {@code ServiceReference} object of the Configuration
+ * Admin service that created this event.
+ *
+ * @return The {@code ServiceReference} object for the Configuration
+ * Admin service that created this event.
+ */
+ public ServiceReference getReference() {
+ return reference;
+ }
+}
diff --git a/configadmin/src/main/java/org/osgi/service/cm/ConfigurationPermission.java b/configadmin/src/main/java/org/osgi/service/cm/ConfigurationPermission.java
new file mode 100644
index 0000000..fb3eb4e
--- /dev/null
+++ b/configadmin/src/main/java/org/osgi/service/cm/ConfigurationPermission.java
@@ -0,0 +1,638 @@
+/*
+ * Copyright (c) OSGi Alliance (2004, 2011). All Rights Reserved.
+ *
+ * Licensed 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.osgi.service.cm;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.security.BasicPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Indicates a bundle's authority to configure bundles or be updated by
+ * Configuration Admin.
+ *
+ * @ThreadSafe
+ * @version $Id: 0d700c494f2dc2bbe05165bf5c79fe185c9f0a4a $
+ * @since 1.2
+ */
+
+public final class ConfigurationPermission extends BasicPermission {
+ static final long serialVersionUID = 5716868734811965383L;
+ /**
+ * Provides permission to create new configurations for other bundles as
+ * well as manipulate them. The action string {@value #CONFIGURE}.
+ */
+ public final static String CONFIGURE = "configure";
+
+ /**
+ * The permission to be updated, that is, act as a Managed Service or
+ * Managed Service Factory. The action string {@value #TARGET}.
+ *
+ * @since 1.4
+ */
+ public final static String TARGET = "target";
+
+ private final static int ACTION_CONFIGURE = 0x00000001;
+ private final static int ACTION_TARGET = 0x00000002;
+ private final static int ACTION_ALL = ACTION_CONFIGURE
+ | ACTION_TARGET;
+ final static int ACTION_NONE = 0;
+
+ /**
+ * The actions mask.
+ */
+ transient int action_mask;
+
+ /**
+ * The actions in canonical form.
+ *
+ * @serial
+ */
+ private volatile String actions = null;
+
+ /**
+ * Parsed name if it includes wildcards: "*"
+ */
+ private transient List substrings;
+
+ /**
+ * Create a new ConfigurationPermission.
+ *
+ * @param name Name of the permission. Wildcards ({@code '*'}) are allowed
+ * in the name. During {@link #implies(Permission)}, the name is
+ * matched to the requested permission using the substring matching
+ * rules used by {@link Filter}s.
+ * @param actions Comma separated list of {@link #CONFIGURE},
+ * {@link #TARGET}.
+ */
+
+ public ConfigurationPermission(String name, String actions) {
+ this(name, parseActions(actions));
+ }
+
+ /**
+ * Package private constructor used by ConfigurationPermissionCollection.
+ *
+ * @param name location string
+ * @param mask action mask
+ */
+ ConfigurationPermission(String name, int mask) {
+ super(name);
+ setTransients(mask);
+ }
+
+ /**
+ * Called by constructors and when deserialized.
+ *
+ * @param mask action mask
+ */
+ private void setTransients(int mask) {
+ if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
+ throw new IllegalArgumentException("invalid action string");
+ }
+ action_mask = mask;
+ substrings = parseSubstring(getName());
+ }
+
+ /**
+ * Parse action string into action mask.
+ *
+ * @param actions Action string.
+ * @return action mask.
+ */
+ private static int parseActions(String actions) {
+ boolean seencomma = false;
+
+ int mask = ACTION_NONE;
+
+ if (actions == null) {
+ return mask;
+ }
+
+ char[] a = actions.toCharArray();
+
+ int i = a.length - 1;
+ if (i < 0)
+ return mask;
+
+ while (i != -1) {
+ char c;
+
+ // skip whitespace
+ while ((i != -1)
+ && ((c = a[i]) == ' ' || c == '\r' || c == '\n'
+ || c == '\f' || c == '\t'))
+ i--;
+
+ // check for the known strings
+ int matchlen;
+
+ if (i >= 5 && (a[i - 5] == 't' || a[i - 5] == 'T')
+ && (a[i - 4] == 'a' || a[i - 4] == 'A')
+ && (a[i - 3] == 'r' || a[i - 3] == 'R')
+ && (a[i - 2] == 'g' || a[i - 2] == 'G')
+ && (a[i - 1] == 'e' || a[i - 1] == 'E')
+ && (a[i] == 't' || a[i] == 'T')) {
+ matchlen = 6;
+ mask |= ACTION_TARGET;
+
+ }
+ else
+ if (i >= 8 && (a[i - 8] == 'c' || a[i - 8] == 'C')
+ && (a[i - 7] == 'o' || a[i - 7] == 'O')
+ && (a[i - 6] == 'n' || a[i - 6] == 'N')
+ && (a[i - 5] == 'f' || a[i - 5] == 'F')
+ && (a[i - 4] == 'i' || a[i - 4] == 'I')
+ && (a[i - 3] == 'g' || a[i - 3] == 'G')
+ && (a[i - 2] == 'u' || a[i - 2] == 'U')
+ && (a[i - 1] == 'r' || a[i - 1] == 'R')
+ && (a[i] == 'e' || a[i] == 'E')) {
+ matchlen = 9;
+ mask |= ACTION_CONFIGURE;
+
+ }
+ else {
+ // parse error
+ throw new IllegalArgumentException("invalid actions: "
+ + actions);
+ }
+
+ // make sure we didn't just match the tail of a word
+ // like "ackbarftarget". Also, skip to the comma.
+ seencomma = false;
+ while (i >= matchlen && !seencomma) {
+ switch (a[i - matchlen]) {
+ case ',' :
+ seencomma = true;
+ /* FALLTHROUGH */
+ case ' ' :
+ case '\r' :
+ case '\n' :
+ case '\f' :
+ case '\t' :
+ break;
+ default :
+ throw new IllegalArgumentException(
+ "invalid permission: " + actions);
+ }
+ i--;
+ }
+
+ // point i at the location of the comma minus one (or -1).
+ i -= matchlen;
+ }
+
+ if (seencomma) {
+ throw new IllegalArgumentException("invalid actions: " + actions);
+ }
+
+ return mask;
+ }
+
+ /**
+ * Parse the name for wildcard processing.
+ *
+ * @param name The name of the permission.
+ * @return {@code null} is the name has no wildcards or a
+ * {@code List<String>} where element is a substring to match or
+ * null for {@code '*'}.
+ */
+ private static List parseSubstring(String name) {
+ if (name.indexOf('*') < 0) {
+ return null;
+ }
+ char[] chars = name.toCharArray();
+ StringBuffer sb = new StringBuffer(chars.length);
+
+ List sub = new ArrayList(10);
+
+ for (int pos = 0; pos < chars.length; pos++) {
+ char c = chars[pos];
+
+ switch (c) {
+ case '*' : {
+ if (sb.length() > 0) {
+ sub.add(sb.toString());
+ }
+ sb.setLength(0);
+ sub.add(null);
+ break;
+ }
+
+ case '\\' : {
+ pos++;
+ if (pos < chars.length) {
+ c = chars[pos];
+ }
+ /* fall through into default */
+ }
+
+ default : {
+ sb.append(c);
+ break;
+ }
+ }
+ }
+ if (sb.length() > 0) {
+ sub.add(sb.toString());
+ }
+
+ int size = sub.size();
+
+ if (size == 0) {
+ return null;
+ }
+
+ if (size == 1) {
+ if (sub.get(0) != null) {
+ return null;
+ }
+ }
+ return sub;
+ }
+
+ /**
+ * Determines if a {@code ConfigurationPermission} object "implies" the
+ * specified permission.
+ *
+ * @param p The target permission to check.
+ * @return {@code true} if the specified permission is implied by this
+ * object; {@code false} otherwise.
+ */
+
+ public boolean implies(Permission p) {
+ if (!(p instanceof ConfigurationPermission)) {
+ return false;
+ }
+ ConfigurationPermission requested = (ConfigurationPermission) p;
+ return implies0(requested, ACTION_NONE);
+ }
+
+ /**
+ * Internal implies method. Used by the implies and the permission
+ * collection implies methods.
+ *
+ * @param requested The requested ConfigurationPermission which has already
+ * be validated as a proper argument.
+ * @param effective The effective actions with which to start.
+ * @return {@code true} if the specified permission is implied by this
+ * object; {@code false} otherwise.
+ */
+ boolean implies0(ConfigurationPermission requested, int effective) {
+ /* check actions first - much faster */
+ effective |= action_mask;
+ final int desired = requested.action_mask;
+ if ((effective & desired) != desired) {
+ return false;
+ }
+ String requestedName = requested.getName();
+ if (substrings == null) {
+ return getName().equals(requestedName);
+ }
+ for (int i = 0, pos = 0, size = substrings.size(); i < size; i++) {
+ String substr = (String) substrings.get(i);
+
+ if (i + 1 < size) /* if this is not that last substr */{
+ if (substr == null) /* * */{
+ String substr2 = (String) substrings.get(i + 1);
+
+ if (substr2 == null) /* ** */
+ continue; /* ignore first star */
+ /* xxx */
+ int index = requestedName.indexOf(substr2, pos);
+ if (index == -1) {
+ return false;
+ }
+
+ pos = index + substr2.length();
+ if (i + 2 < size) // if there are more
+ // substrings, increment
+ // over the string we just
+ // matched; otherwise need
+ // to do the last substr
+ // check
+ i++;
+ }
+ else /* xxx */{
+ int len = substr.length();
+ if (requestedName.regionMatches(pos, substr, 0, len)) {
+ pos += len;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+ else /* last substr */{
+ if (substr == null) /* * */{
+ return true;
+ }
+ /* xxx */
+ return requestedName.endsWith(substr);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines the equality of two {@code ConfigurationPermission} objects.
+ * <p>
+ * Two {@code ConfigurationPermission} objects are equal.
+ *
+ * @param obj The object being compared for equality with this object.
+ * @return {@code true} if {@code obj} is equivalent to this
+ * {@code ConfigurationPermission}; {@code false} otherwise.
+ */
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (!(obj instanceof ConfigurationPermission)) {
+ return false;
+ }
+
+ ConfigurationPermission cp = (ConfigurationPermission) obj;
+
+ return (action_mask == cp.action_mask)
+ && getName().equals(cp.getName());
+ }
+
+ /**
+ * Returns the hash code value for this object.
+ *
+ * @return Hash code value for this object.
+ */
+
+ public int hashCode() {
+ int h = 31 * 17 + getName().hashCode();
+ h = 31 * h + getActions().hashCode();
+ return h;
+ }
+
+ /**
+ * Returns the canonical string representation of the
+ * {@code ConfigurationPermission} actions.
+ *
+ * <p>
+ * Always returns present {@code ConfigurationPermission} actions in the
+ * following order: {@value #CONFIGURE}, {@value #TARGET}
+ *
+ * @return Canonical string representation of the
+ * {@code ConfigurationPermission} actions.
+ */
+ public String getActions() {
+ String result = actions;
+ if (result == null) {
+ StringBuffer sb = new StringBuffer();
+ boolean comma = false;
+
+ int mask = action_mask;
+ if ((mask & ACTION_CONFIGURE) == ACTION_CONFIGURE) {
+ sb.append(CONFIGURE);
+ comma = true;
+ }
+
+ if ((mask & ACTION_TARGET) == ACTION_TARGET) {
+ if (comma)
+ sb.append(',');
+ sb.append(TARGET);
+ }
+
+ actions = result = sb.toString();
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a new {@code PermissionCollection} object suitable for storing
+ * {@code ConfigurationPermission}s.
+ *
+ * @return A new {@code PermissionCollection} object.
+ */
+ public PermissionCollection newPermissionCollection() {
+ return new ConfigurationPermissionCollection();
+ }
+
+ /**
+ * WriteObject is called to save the state of this permission object to a
+ * stream. The actions are serialized, and the superclass takes care of the
+ * name.
+ */
+ private synchronized void writeObject(java.io.ObjectOutputStream s)
+ throws IOException {
+ // Write out the actions. The superclass takes care of the name
+ // call getActions to make sure actions field is initialized
+ if (actions == null)
+ getActions();
+ s.defaultWriteObject();
+ }
+
+ /**
+ * readObject is called to restore the state of this permission from a
+ * stream.
+ */
+ private synchronized void readObject(java.io.ObjectInputStream s)
+ throws IOException, ClassNotFoundException {
+ // Read in the data, then initialize the transients
+ s.defaultReadObject();
+ setTransients(parseActions(actions));
+ }
+}
+
+/**
+ * Stores a set of {@code ConfigurationPermission} permissions.
+ *
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ */
+final class ConfigurationPermissionCollection extends PermissionCollection {
+ static final long serialVersionUID = -6917638867081695839L;
+ /**
+ * Collection of permissions.
+ *
+ * @serial
+ * @GuardedBy this
+ */
+ private Map permissions;
+
+ /**
+ * Boolean saying if "*" is in the collection.
+ *
+ * @serial
+ * @GuardedBy this
+ */
+ private boolean all_allowed;
+
+ /**
+ * Creates an empty {@code ConfigurationPermissionCollection} object.
+ *
+ */
+ public ConfigurationPermissionCollection() {
+ permissions = new HashMap();
+ all_allowed = false;
+ }
+
+ /**
+ * Adds the specified permission to the
+ * {@code ConfigurationPermissionCollection}. The key for the hash is the
+ * interface name of the service.
+ *
+ * @param permission The {@code Permission} object to add.
+ *
+ * @exception IllegalArgumentException If the permission is not an
+ * {@code ConfigurationPermission}.
+ *
+ * @exception SecurityException If this ConfigurationPermissionCollection
+ * object has been marked read-only.
+ */
+
+ public void add(Permission permission) {
+ if (!(permission instanceof ConfigurationPermission)) {
+ throw new IllegalArgumentException("invalid permission: "
+ + permission);
+ }
+
+ if (isReadOnly())
+ throw new SecurityException("attempt to add a Permission to a "
+ + "readonly PermissionCollection");
+
+ final ConfigurationPermission cp = (ConfigurationPermission) permission;
+ final String name = cp.getName();
+ synchronized (this) {
+ Map pc = permissions;
+ final ConfigurationPermission existing = (ConfigurationPermission) pc.get(name);
+ if (existing != null) {
+ final int oldMask = existing.action_mask;
+ final int newMask = cp.action_mask;
+ if (oldMask != newMask) {
+ pc.put(name, new ConfigurationPermission(name, oldMask
+ | newMask));
+ }
+ }
+ else {
+ pc.put(name, cp);
+ }
+
+ if (!all_allowed) {
+ if (name.equals("*")) {
+ all_allowed = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Determines if the specified permissions implies the permissions expressed
+ * in {@code permission}.
+ *
+ * @param permission The Permission object to compare with this
+ * {@code ConfigurationPermission} object.
+ * @return {@code true} if {@code permission} is a proper subset of a
+ * permission in the set; {@code false} otherwise.
+ */
+ public boolean implies(Permission permission) {
+ if (!(permission instanceof ConfigurationPermission)) {
+ return false;
+ }
+ final ConfigurationPermission requested = (ConfigurationPermission) permission;
+ int effective = ConfigurationPermission.ACTION_NONE;
+
+ Collection perms;
+ synchronized (this) {
+ Map pc = permissions;
+ /* short circuit if the "*" Permission was added */
+ if (all_allowed) {
+ ConfigurationPermission cp = (ConfigurationPermission) pc.get("*");
+ if (cp != null) {
+ effective |= cp.action_mask;
+ final int desired = requested.action_mask;
+ if ((effective & desired) == desired) {
+ return true;
+ }
+ }
+ }
+ perms = pc.values();
+ }
+ /* iterate one by one over permissions */
+ for (Iterator permI = perms.iterator(); permI.hasNext(); ) {
+ ConfigurationPermission perm = (ConfigurationPermission) permI.next();
+ if (perm.implies0(requested, effective)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns an enumeration of all {@code ConfigurationPermission} objects in
+ * the container.
+ *
+ * @return Enumeration of all {@code ConfigurationPermission} objects.
+ */
+ public synchronized Enumeration elements() {
+ List all = new ArrayList(permissions.values());
+ return Collections.enumeration(all);
+ }
+
+ /* serialization logic */
+ private static final ObjectStreamField[] serialPersistentFields = {
+ new ObjectStreamField("hasElement", Boolean.TYPE),
+ new ObjectStreamField("permissions", HashMap.class),
+ new ObjectStreamField("all_allowed", Boolean.TYPE) };
+
+ private synchronized void writeObject(ObjectOutputStream out)
+ throws IOException {
+ ObjectOutputStream.PutField pfields = out.putFields();
+ pfields.put("hasElement", false);
+ pfields.put("permissions", permissions);
+ pfields.put("all_allowed", all_allowed);
+ out.writeFields();
+ }
+
+ private synchronized void readObject(java.io.ObjectInputStream in)
+ throws IOException, ClassNotFoundException {
+ ObjectInputStream.GetField gfields = in.readFields();
+ boolean hasElement = gfields.get("hasElement", false);
+ if (hasElement) { // old format
+ permissions = new HashMap();
+ permissions.put("*", new ConfigurationPermission("*",
+ ConfigurationPermission.CONFIGURE));
+ all_allowed = true;
+ }
+ else {
+ permissions = (HashMap) gfields
+ .get("permissions",
+ new HashMap());
+ all_allowed = gfields.get("all_allowed", false);
+ }
+ }
+}
diff --git a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBaseTest.java b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBaseTest.java
index ccb8f6f..9fb9c42 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBaseTest.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBaseTest.java
@@ -40,6 +40,12 @@
public class ConfigurationBaseTest extends ConfigurationTestBase
{
+ static
+ {
+ // uncomment to enable debugging of this test class
+ // paxRunnerVmOption = DEBUG_VM_OPTION;
+ }
+
@Test
public void test_basic_configuration_configure_then_start() throws BundleException, IOException
{
diff --git a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBindingTest.java b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBindingTest.java
index 159c588..63f0c70 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBindingTest.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBindingTest.java
@@ -39,6 +39,13 @@
public class ConfigurationBindingTest extends ConfigurationTestBase
{
+ static
+ {
+ // uncomment to enable debugging of this test class
+ // paxRunnerVmOption = DEBUG_VM_OPTION;
+ }
+
+
@Test
public void test_configuration_unbound_on_uninstall() throws BundleException
{
@@ -445,6 +452,7 @@
// remove the static binding and assert still bound
config.setBundleLocation( null );
+ delay();
TestCase.assertEquals( location, config.getBundleLocation() );
// uninstall bundle and assert configuration unbound
@@ -580,33 +588,13 @@
// ==> configuration is bound to locationB
TestCase.assertEquals( locationB, config.getBundleLocation() );
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the bundle location.
- * <p>
- * Therefore, we have two sets one with re-dispatch expectation and
- * one without re-dispatch expectation.
- */
- if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
- {
- // ==> configuration removed from service ms1
- TestCase.assertNull( testerA1.props );
- TestCase.assertEquals( 2, testerA1.numManagedServiceUpdatedCalls );
+ // ==> configuration removed from service ms1
+ TestCase.assertNull( testerA1.props );
+ TestCase.assertEquals( 2, testerA1.numManagedServiceUpdatedCalls );
- // ==> configuration supplied to the service ms2
- TestCase.assertNotNull( testerB1.props );
- TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
- }
- else
- {
- // ==> configuration remains for service ms1
- TestCase.assertNotNull( testerA1.props );
- TestCase.assertEquals( 1, testerA1.numManagedServiceUpdatedCalls );
-
- // ==> configuration not supplied to the service ms2
- TestCase.assertNull( testerB1.props );
- TestCase.assertEquals( 0, testerB1.numManagedServiceUpdatedCalls );
- }
+ // ==> configuration supplied to the service ms2
+ TestCase.assertNotNull( testerB1.props );
+ TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
}
@@ -650,33 +638,13 @@
// ==> configuration is bound to locationB
TestCase.assertEquals( locationB, config.getBundleLocation() );
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the bundle location.
- * <p>
- * Therefore, we have two sets one with re-dispatch expectation and
- * one without re-dispatch expectation.
- */
- if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
- {
- // ==> configuration removed from service ms1
- TestCase.assertNull( testerA1.props );
- TestCase.assertEquals( 2, testerA1.numManagedServiceUpdatedCalls );
+ // ==> configuration removed from service ms1
+ TestCase.assertNull( testerA1.props );
+ TestCase.assertEquals( 2, testerA1.numManagedServiceUpdatedCalls );
- // ==> configuration supplied to the service ms2
- TestCase.assertNotNull( testerB1.props );
- TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
- }
- else
- {
- // ==> configuration remains for service ms1
- TestCase.assertNotNull( testerA1.props );
- TestCase.assertEquals( 1, testerA1.numManagedServiceUpdatedCalls );
-
- // ==> configuration not supplied to the service ms2
- TestCase.assertNull( testerB1.props );
- TestCase.assertEquals( 0, testerB1.numManagedServiceUpdatedCalls );
- }
+ // ==> configuration supplied to the service ms2
+ TestCase.assertNotNull( testerB1.props );
+ TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
// 6. Update configuration now
config.update();
@@ -684,7 +652,7 @@
// ==> configuration supplied to the service ms2
TestCase.assertNotNull( testerB1.props );
- TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
+ TestCase.assertEquals( 2, testerB1.numManagedServiceUpdatedCalls );
}
@@ -726,35 +694,14 @@
// ==> configuration is bound to locationB
TestCase.assertEquals( locationB, config.getBundleLocation() );
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the bundle location.
- * <p>
- * Therefore, we have two sets one with re-dispatch expectation and
- * one without re-dispatch expectation.
- */
- if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
- {
- // ==> configuration removed from service ms1
- TestCase.assertNull( testerA1.configs.get( pid ));
- TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
- TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryDeleteCalls );
+ // ==> configuration removed from service ms1
+ TestCase.assertNull( testerA1.configs.get( pid ));
+ TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
+ TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryDeleteCalls );
- // ==> configuration supplied to the service ms2
- TestCase.assertNotNull( testerB1.configs.get( pid ) );
- TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
- }
- else
- {
- // ==> configuration not removed from service ms1
- TestCase.assertNotNull( testerA1.configs.get( pid ));
- TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
- TestCase.assertEquals( 0, testerA1.numManagedServiceFactoryDeleteCalls );
-
- // ==> configuration not supplied to the service ms2
- TestCase.assertNull( testerB1.configs.get( pid ) );
- TestCase.assertEquals( 0, testerB1.numManagedServiceFactoryUpdatedCalls );
- }
+ // ==> configuration supplied to the service ms2
+ TestCase.assertNotNull( testerB1.configs.get( pid ) );
+ TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
// 6. Update configuration now
config.update();
@@ -762,7 +709,7 @@
// ==> configuration supplied to the service ms2
TestCase.assertNotNull( testerB1.configs.get( pid ) );
- TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
+ TestCase.assertEquals( 2, testerB1.numManagedServiceFactoryUpdatedCalls );
}
@@ -808,35 +755,14 @@
// ==> configuration is bound to locationB
TestCase.assertEquals( locationB, config.getBundleLocation() );
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the bundle location.
- * <p>
- * Therefore, we have two sets one with re-dispatch expectation and
- * one without re-dispatch expectation.
- */
- if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
- {
- // ==> configuration removed from service ms1
- TestCase.assertNull( testerA1.configs.get( pid ));
- TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
- TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryDeleteCalls );
+ // ==> configuration removed from service ms1
+ TestCase.assertNull( testerA1.configs.get( pid ));
+ TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
+ TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryDeleteCalls );
- // ==> configuration supplied to the service ms2
- TestCase.assertNotNull( testerB1.configs.get( pid ) );
- TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
- }
- else
- {
- // ==> configuration not removed from service ms1
- TestCase.assertNotNull( testerA1.configs.get( pid ));
- TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
- TestCase.assertEquals( 0, testerA1.numManagedServiceFactoryDeleteCalls );
-
- // ==> configuration not supplied to the service ms2
- TestCase.assertNull( testerB1.configs.get( pid ) );
- TestCase.assertEquals( 0, testerB1.numManagedServiceFactoryUpdatedCalls );
- }
+ // ==> configuration supplied to the service ms2
+ TestCase.assertNotNull( testerB1.configs.get( pid ) );
+ TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
// 6. Update configuration now
config.update();
@@ -844,7 +770,7 @@
// ==> configuration supplied to the service ms2
TestCase.assertNotNull( testerB1.configs.get( pid ) );
- TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
+ TestCase.assertEquals( 2, testerB1.numManagedServiceFactoryUpdatedCalls );
}
@@ -887,31 +813,12 @@
bundleA.uninstall();
delay();
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the bundle location.
- * <p>
- * Therefore, we have two sets one with re-dispatch expectation and
- * one without re-dispatch expectation.
- */
- if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
- {
- // ==> configuration is bound to locationB
- TestCase.assertEquals( locationB, config.getBundleLocation() );
+ // ==> configuration is bound to locationB
+ TestCase.assertEquals( locationB, config.getBundleLocation() );
- // ==> configuration supplied to the service ms2
- TestCase.assertNotNull( testerB1.props );
- TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
- }
- else
- {
- // ==> configuration is unbound
- TestCase.assertNull( config.getBundleLocation() );
-
- // ==> configuration not supplied to the service ms2
- TestCase.assertNull( testerB1.props );
- TestCase.assertEquals( 0, testerB1.numManagedServiceUpdatedCalls );
- }
+ // ==> configuration supplied to the service ms2
+ TestCase.assertNotNull( testerB1.props );
+ TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
// 6. Update configuration now
config.update();
@@ -919,7 +826,7 @@
// ==> configuration supplied to the service ms2
TestCase.assertNotNull( testerB1.props );
- TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
+ TestCase.assertEquals( 2, testerB1.numManagedServiceUpdatedCalls );
}
@@ -962,31 +869,12 @@
bundleA.uninstall();
delay();
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the bundle location.
- * <p>
- * Therefore, we have two sets one with re-dispatch expectation and
- * one without re-dispatch expectation.
- */
- if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
- {
- // ==> configuration is bound to locationB
- TestCase.assertEquals( locationB, config.getBundleLocation() );
+ // ==> configuration is bound to locationB
+ TestCase.assertEquals( locationB, config.getBundleLocation() );
- // ==> configuration supplied to the service ms2
- TestCase.assertNotNull( testerB1.configs.get( pid ) );
- TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
- }
- else
- {
- // ==> configuration is unbound
- TestCase.assertNull( config.getBundleLocation() );
-
- // ==> configuration not supplied to the service ms2
- TestCase.assertNull( testerB1.configs.get( pid ) );
- TestCase.assertEquals( 0, testerB1.numManagedServiceFactoryUpdatedCalls );
- }
+ // ==> configuration supplied to the service ms2
+ TestCase.assertNotNull( testerB1.configs.get( pid ) );
+ TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
// 6. Update configuration now
config.update();
@@ -994,6 +882,6 @@
// ==> configuration supplied to the service ms2
TestCase.assertNotNull( testerB1.configs.get( pid ) );
- TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
+ TestCase.assertEquals( 2, testerB1.numManagedServiceFactoryUpdatedCalls );
}
}
diff --git a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTestBase.java b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTestBase.java
index e012e24..f76e9af 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTestBase.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTestBase.java
@@ -29,7 +29,9 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Dictionary;
+import java.util.HashSet;
import java.util.Hashtable;
+import java.util.Set;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
@@ -60,24 +62,6 @@
public abstract class ConfigurationTestBase
{
- /**
- * There is currently an open issue in the specification in whether a
- * call to Configuration.setBundleLocation() might trigger a configuration
- * update or not.
- * We have test cases in our integration test suite for both cases. To
- * enable the respective tests set this field accordingly:
- * <dl>
- * <dt>false</dt>
- * <dd>Expect configuration to <b>NOT</b> be redispatched. That is existing
- * configurations are kept and other services are not updated</dd>
- * <dt>true</dt>
- * <dd>Expect configuration to be redispatched. That is existing configuration
- * is revoked (update(null) or delete calls) and new matching services are
- * updated.</dd>
- * </dl>
- */
- public static final boolean REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION = false;
-
// the name of the system property providing the bundle file to be installed and tested
protected static final String BUNDLE_JAR_SYS_PROP = "project.bundle.file";
@@ -99,6 +83,8 @@
protected ServiceTracker configAdminTracker;
+ private Set<String> configurations = new HashSet<String>();
+
protected static final String PROP_NAME = "theValue";
protected static final Dictionary<String, String> theConfig;
@@ -121,6 +107,7 @@
}
final Option[] base = options(
+ /* CoreOptions.allFrameworks(), */
provision(
CoreOptions.bundle( bundleFile.toURI().toString() ),
mavenBundle( "org.ops4j.pax.swissbox", "pax-swissbox-tinybundles", "1.0.0" )
@@ -148,6 +135,11 @@
bundle.uninstall();
}
+ for ( String pid : configurations )
+ {
+ deleteConfig( pid );
+ }
+
configAdminTracker.close();
configAdminTracker = null;
}
@@ -299,7 +291,7 @@
final ConfigurationAdmin ca = getConfigurationAdmin();
try
{
- final Configuration config = ca.createFactoryConfiguration( factoryPid, null );
+ final Configuration config = ca.createFactoryConfiguration( factoryPid, location );
if ( withProps )
{
config.update( theConfig );
@@ -345,6 +337,7 @@
final ConfigurationAdmin ca = getConfigurationAdmin();
try
{
+ configurations.remove( pid );
final Configuration config = ca.getConfiguration( pid );
config.delete();
}
@@ -366,6 +359,7 @@
{
for ( Configuration configuration : configs )
{
+ configurations.remove( configuration.getPid() );
configuration.delete();
}
}
diff --git a/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServiceFactoryPIDTest.java b/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServiceFactoryPIDTest.java
index 1ce490e..55d9eb0 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServiceFactoryPIDTest.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServiceFactoryPIDTest.java
@@ -39,6 +39,11 @@
@RunWith(JUnit4TestRunner.class)
public class MultiServiceFactoryPIDTest extends ConfigurationTestBase
{
+ static
+ {
+ // uncomment to enable debugging of this test class
+ // paxRunnerVmOption = DEBUG_VM_OPTION;
+ }
@Test
public void test_two_services_same_pid_in_same_bundle_configure_before_registration() throws BundleException
@@ -61,7 +66,7 @@
// assert activater has configuration (two calls, one per pid)
TestCase.assertNotNull( "Expect Properties after Service Registration", tester.configs.get( pid ) );
- TestCase.assertEquals( "Expect a single update call", 2, tester.numManagedServiceFactoryUpdatedCalls );
+ TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceFactoryUpdatedCalls );
TestCase.assertEquals( bundle.getLocation(), config.getBundleLocation() );
@@ -165,23 +170,10 @@
delay();
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the bundle location.
- * <p>
- * Therefore, we have two sets one with re-dispatch expectation and
- * one without re-dispatch expectation.
- */
- if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
- {
- // expect configuration reassigned
- TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
- }
- else
- {
- // expected configuration unbound
- TestCase.assertNull( config.getBundleLocation() );
- }
+ // expect configuration reassigned
+ TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
+ TestCase.assertNotNull( "Expect Properties after Configuration Redispatch", tester2.configs.get( pid ) );
+ TestCase.assertEquals( "Expect update call after Configuration Redispatch", 1, tester2.numManagedServiceFactoryUpdatedCalls );
// remove the configuration for good
deleteConfig( pid );
@@ -230,78 +222,27 @@
delay();
TestCase.assertEquals( factoryPid, config.getFactoryPid() );
- TestCase.assertNotNull( config.getBundleLocation() );
- if ( bundle.getLocation().equals( config.getBundleLocation() ) )
- {
- // configuration assigned to the first bundle
- TestCase.assertNotNull( "Expect Properties after Service Registration", tester.configs.get( pid ) );
- TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceFactoryUpdatedCalls );
+ TestCase.assertEquals(
+ "Configuration must be bound to second bundle because the service has higher ranking",
+ bundle.getLocation(), config.getBundleLocation() );
- TestCase.assertTrue( "Expect Properties after Service Registration", tester2.configs.isEmpty() );
- TestCase.assertEquals( "Expect a single update call", 0, tester2.numManagedServiceFactoryUpdatedCalls );
+ // configuration assigned to the first bundle
+ TestCase.assertNotNull( "Expect Properties after Service Registration", tester.configs.get( pid ) );
+ TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceFactoryUpdatedCalls );
- bundle.uninstall();
- bundle = null;
+ TestCase.assertTrue( "Expect Properties after Service Registration", tester2.configs.isEmpty() );
+ TestCase.assertEquals( "Expect a single update call", 0, tester2.numManagedServiceFactoryUpdatedCalls );
- delay();
+ bundle.uninstall();
+ bundle = null;
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the bundle location.
- * <p>
- * Therefore, we have two sets one with re-dispatch expectation and
- * one without re-dispatch expectation.
- */
- if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
- {
- // expect configuration reassigned
- TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
- }
- else
- {
- // expected configuration unbound
- TestCase.assertNull( config.getBundleLocation() );
- }
- }
- else if ( bundle2.getLocation().equals( config.getBundleLocation() ) )
- {
- // configuration assigned to the second bundle
- // assert activater has configuration (two calls, one per pid)
- TestCase.assertNotNull( "Expect Properties after Service Registration", tester2.configs.get( pid ) );
- TestCase.assertEquals( "Expect a single update call", 1, tester2.numManagedServiceFactoryUpdatedCalls );
+ delay();
- TestCase.assertTrue( "Expect Properties after Service Registration", tester.configs.isEmpty() );
- TestCase.assertEquals( "Expect a single update call", 0, tester.numManagedServiceFactoryUpdatedCalls );
-
- bundle2.uninstall();
- bundle2 = null;
-
- delay();
-
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the bundle location.
- * <p>
- * Therefore, we have two sets one with re-dispatch expectation and
- * one without re-dispatch expectation.
- */
- if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
- {
- // expect configuration reassigned
- TestCase.assertEquals( bundle.getLocation(), config.getBundleLocation() );
- }
- else
- {
- // expected configuration unbound
- TestCase.assertNull( config.getBundleLocation() );
- }
- }
- else
- {
- // configuration assigned to some other bundle ... fail
- TestCase.fail( "Configuration assigned to unexpected bundle " + config.getBundleLocation() );
- }
+ // expect configuration reassigned
+ TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
+ TestCase.assertNotNull( "Expect Properties after Configuration Redispatch", tester2.configs.get( pid ) );
+ TestCase.assertEquals( "Expect a single update call after Configuration Redispatch", 1, tester2.numManagedServiceFactoryUpdatedCalls );
// remove the configuration for good
deleteConfig( pid );
diff --git a/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServicePIDTest.java b/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServicePIDTest.java
index 9ff808a..f4eedda 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServicePIDTest.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServicePIDTest.java
@@ -39,6 +39,11 @@
@RunWith(JUnit4TestRunner.class)
public class MultiServicePIDTest extends ConfigurationTestBase
{
+ static
+ {
+ // uncomment to enable debugging of this test class
+ // paxRunnerVmOption = DEBUG_VM_OPTION;
+ }
@Test
public void test_two_services_same_pid_in_same_bundle_configure_before_registration() throws BundleException
@@ -150,7 +155,7 @@
// expect first activator to have received properties
- // assert first bundle has configuration (two calls, one per srv)
+ // assert first bundle has configuration (one calls, one per srv)
TestCase.assertNotNull( "Expect Properties after Service Registration", tester.props );
TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceUpdatedCalls );
@@ -166,23 +171,16 @@
delay();
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the bundle location.
- * <p>
- * Therefore, we have two sets one with re-dispatch expectation and
- * one without re-dispatch expectation.
- */
- if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
- {
- // expect configuration reassigned
- TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
- }
- else
- {
- // expected configuration unbound
- TestCase.assertNull( config.getBundleLocation() );
- }
+ // after uninstallation, the configuration is redispatched
+ // due to the dynamic binding being removed
+
+ // expect configuration reassigned
+ TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
+
+ // assert second bundle now has the configuration
+ TestCase.assertNotNull( "Expect Properties after Configuration redispatch", tester2.props );
+ TestCase.assertEquals( "Expect a single update call after Configuration redispatch", 1,
+ tester2.numManagedServiceUpdatedCalls );
// remove the configuration for good
deleteConfig( pid );
@@ -231,78 +229,33 @@
final Configuration config = getConfiguration( pid );
TestCase.assertEquals( pid, config.getPid() );
- TestCase.assertNotNull( config.getBundleLocation() );
- if ( bundle.getLocation().equals( config.getBundleLocation() ) )
- {
- // configuration assigned to the first bundle
- TestCase.assertNotNull( "Expect Properties after Service Registration", tester.props );
- TestCase.assertEquals( "Expect a single update call", 2, tester.numManagedServiceUpdatedCalls );
+ TestCase.assertEquals(
+ "Configuration must be bound to first bundle because the service has higher ranking",
+ bundle.getLocation(), config.getBundleLocation() );
- TestCase.assertNull( "Expect Properties after Service Registration", tester2.props );
- TestCase.assertEquals( "Expect a single update call", 1, tester2.numManagedServiceUpdatedCalls );
+ // configuration assigned to the first bundle
+ TestCase.assertNotNull( "Expect Properties after Service Registration", tester.props );
+ TestCase.assertEquals( "Expect a single update call", 2, tester.numManagedServiceUpdatedCalls );
- bundle.uninstall();
- bundle = null;
+ TestCase.assertNull( "Expect Properties after Service Registration", tester2.props );
+ TestCase.assertEquals( "Expect a single update call", 1, tester2.numManagedServiceUpdatedCalls );
- delay();
+ bundle.uninstall();
+ bundle = null;
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the bundle location.
- * <p>
- * Therefore, we have two sets one with re-dispatch expectation and
- * one without re-dispatch expectation.
- */
- if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
- {
- // expect configuration reassigned
- TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
- }
- else
- {
- // expected configuration unbound
- TestCase.assertNull( config.getBundleLocation() );
- }
- }
- else if ( bundle2.getLocation().equals( config.getBundleLocation() ) )
- {
- // configuration assigned to the second bundle
- // assert activater has configuration (two calls, one per pid)
- TestCase.assertNotNull( "Expect Properties after Service Registration", tester2.props );
- TestCase.assertEquals( "Expect a single update call", 2, tester2.numManagedServiceUpdatedCalls );
+ delay();
- TestCase.assertNull( "Expect Properties after Service Registration", tester.props );
- TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceUpdatedCalls );
+ // after uninstallation, the configuration is redispatched
+ // due to the dynamic binding being removed
- bundle2.uninstall();
- bundle2 = null;
+ // expect configuration reassigned
+ TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
- delay();
-
- /*
- * According to BJ Hargrave configuration is not re-dispatched
- * due to setting the bundle location.
- * <p>
- * Therefore, we have two sets one with re-dispatch expectation and
- * one without re-dispatch expectation.
- */
- if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
- {
- // expect configuration reassigned
- TestCase.assertEquals( bundle.getLocation(), config.getBundleLocation() );
- }
- else
- {
- // expected configuration unbound
- TestCase.assertNull( config.getBundleLocation() );
- }
- }
- else
- {
- // configuration assigned to some other bundle ... fail
- TestCase.fail( "Configuration assigned to unexpected bundle " + config.getBundleLocation() );
- }
+ // assert second bundle now has the configuration
+ TestCase.assertNotNull( "Expect Properties after Configuration redispatch", tester2.props );
+ TestCase.assertEquals( "Expect a single update call after Configuration redispatch", 2,
+ tester2.numManagedServiceUpdatedCalls );
// remove the configuration for good
deleteConfig( pid );