FELIX-1488 configuration replacement if static bundle binding is
replaced as described in https://issues.apache.org/jira/browse/FELIX-1488?focusedCommentId=12744909&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#action_12744909
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@805716 13f79535-47bb-0310-9956-ffa450edef68
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 3385c3c..d5e72d6 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
@@ -154,9 +154,26 @@
void setStaticBundleLocation( final String bundleLocation )
{
+ // 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
+ boolean replace = ( this instanceof ConfigurationImpl ) && ( bundleLocation != null );
+ if ( replace && getDynamicBundleLocation() != null && !bundleLocation.equals( getDynamicBundleLocation() ) )
+ {
+ // remove configuration from current managed service [factory]
+ getConfigurationManager().deleted( ( ConfigurationImpl ) this, false );
+ }
+
// 104.15.2.8 The bundle location will be set persistently
this.staticBundleLocation = bundleLocation;
storeSilently();
+
+ // check whether we have to assign the configuration to new targets
+ if ( replace )
+ {
+ getConfigurationManager().updated( ( ConfigurationImpl ) this, false );
+ }
}
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 d22214b..e3288ed 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
@@ -123,7 +123,7 @@
{
this.isDeleted = true;
getPersistenceManager().delete( this.getPid() );
- getConfigurationManager().deleted( this );
+ getConfigurationManager().deleted( this, true );
}
@@ -190,7 +190,7 @@
configureFromPersistence( properties );
- getConfigurationManager().updated( this );
+ getConfigurationManager().updated( this, true );
}
}
@@ -241,7 +241,7 @@
// finally assign the configuration for use
configure( newProperties );
- getConfigurationManager().updated( this );
+ getConfigurationManager().updated( this, true );
}
}
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 1defa50..06b270f 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
@@ -558,17 +558,17 @@
}
- void deleted( ConfigurationImpl config )
+ void deleted( ConfigurationImpl config, boolean fireEvent )
{
// remove the configuration from the cache
removeConfiguration( config );
- updateThread.schedule( new DeleteConfiguration( config ) );
+ updateThread.schedule( new DeleteConfiguration( config, fireEvent ) );
}
- void updated( ConfigurationImpl config )
+ void updated( ConfigurationImpl config, boolean fireEvent )
{
- updateThread.schedule( new UpdateConfiguration( config ) );
+ updateThread.schedule( new UpdateConfiguration( config, fireEvent ) );
}
@@ -1276,14 +1276,15 @@
{
private final ConfigurationImpl config;
-
private final Dictionary properties;
+ private final boolean fireEvent;
- UpdateConfiguration( final ConfigurationImpl config )
+ UpdateConfiguration( final ConfigurationImpl config, boolean fireEvent )
{
this.config = config;
this.properties = config.getProperties( true );
+ this.fireEvent = fireEvent;
}
@@ -1299,7 +1300,8 @@
{
// find the primary configuration owner
final ServiceReference ownerRef = getOwner( config, srList );
- final String bundleLocation = ownerRef.getBundle().getLocation();
+ final String bundleLocation = ( ownerRef != null ) ? ownerRef.getBundle().getLocation()
+ : config.getBundleLocation();
// if the configuration is unbound, bind to owner
if ( config.getBundleLocation() == null )
@@ -1361,7 +1363,8 @@
{
// find the primary configuration owner
final ServiceReference ownerRef = getOwner( config, srList );
- final String bundleLocation = ownerRef.getBundle().getLocation();
+ final String bundleLocation = ( ownerRef != null ) ? ownerRef.getBundle().getLocation()
+ : config.getBundleLocation();
// if the configuration is unbound, bind to owner
if ( config.getBundleLocation() == null )
@@ -1437,7 +1440,10 @@
{
// the update event has to be sent regardless of whether the
// configuration was updated in a managed service or not
- fireConfigurationEvent( ConfigurationEvent.CM_UPDATED, config.getPid(), config.getFactoryPid() );
+ if ( fireEvent )
+ {
+ fireConfigurationEvent( ConfigurationEvent.CM_UPDATED, config.getPid(), config.getFactoryPid() );
+ }
}
}
@@ -1455,8 +1461,25 @@
}
}
}
-
// 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.getBundleLocation();
+ 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;
+ }
+ // 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 );
@@ -1476,16 +1499,19 @@
private final String pid;
private final String factoryPid;
private final String configLocation;
+ private final boolean fireEvent;
- DeleteConfiguration( ConfigurationImpl config )
+ DeleteConfiguration( ConfigurationImpl config, boolean fireEvent )
{
this.pid = config.getPid();
this.factoryPid = config.getFactoryPid();
this.configLocation = config.getBundleLocation();
+ this.fireEvent = fireEvent;
// immediately unbind the configuration
config.setDynamicBundleLocation( null );
+ config.setServiceReference( null );
}
@@ -1571,7 +1597,10 @@
{
// the delete event has to be sent regardless of whether the
// configuration was updated in a managed service or not
- fireConfigurationEvent( ConfigurationEvent.CM_DELETED, pid, factoryPid );
+ if ( fireEvent )
+ {
+ fireConfigurationEvent( ConfigurationEvent.CM_DELETED, pid, factoryPid );
+ }
}
}
diff --git a/configadmin/src/test/java/org/apache/felix/cm/impl/MockConfigurationManager.java b/configadmin/src/test/java/org/apache/felix/cm/impl/MockConfigurationManager.java
index 3ec434e..527e348 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/impl/MockConfigurationManager.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/impl/MockConfigurationManager.java
@@ -22,13 +22,13 @@
public class MockConfigurationManager extends ConfigurationManager
{
- void updated( ConfigurationImpl config )
+ void updated( ConfigurationImpl config, boolean fireEvent )
{
// do nothing, might register the update call
}
- void deleted( ConfigurationImpl config )
+ void deleted( ConfigurationImpl config, boolean fireEvent )
{
// do nothing, might register the update call
}
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 981c1a4..1177d7f 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
@@ -23,7 +23,10 @@
import java.util.Hashtable;
import junit.framework.TestCase;
+import org.apache.felix.cm.integration.helper.ManagedServiceFactoryTestActivator;
+import org.apache.felix.cm.integration.helper.ManagedServiceFactoryTestActivator2;
import org.apache.felix.cm.integration.helper.ManagedServiceTestActivator;
+import org.apache.felix.cm.integration.helper.ManagedServiceTestActivator2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
@@ -36,7 +39,7 @@
public class ConfigurationBindingTest extends ConfigurationTestBase
{
- @Test
+ @Test
public void test_configuration_unbound_on_uninstall() throws BundleException
{
String pid = "test.pid";
@@ -105,7 +108,7 @@
}
- @Test
+ @Test
public void test_configuration_unbound_on_uninstall_with_cm_restart() throws BundleException
{
final String pid = "test.pid";
@@ -179,7 +182,7 @@
}
- @Test
+ @Test
public void test_not_updated_new_configuration_not_bound_after_bundle_uninstall() throws IOException,
BundleException
{
@@ -220,7 +223,7 @@
}
- @Test
+ @Test
public void test_create_with_location_unbind_before_service_supply() throws BundleException, IOException
{
@@ -272,7 +275,7 @@
}
- @Test
+ @Test
public void test_statically_bound() throws BundleException
{
final String pid = "test_statically_bound";
@@ -317,7 +320,7 @@
}
- @Test
+ @Test
public void test_static_binding_and_unbinding() throws BundleException
{
final String pid = "test_static_binding_and_unbinding";
@@ -360,7 +363,7 @@
}
- @Test
+ @Test
public void test_dynamic_binding_and_unbinding() throws BundleException
{
final String pid = "test_dynamic_binding_and_unbinding";
@@ -406,7 +409,7 @@
}
- @Test
+ @Test
public void test_static_binding() throws BundleException
{
final String pid = "test_static_binding";
@@ -446,7 +449,7 @@
}
- @Test
+ @Test
public void test_two_bundles_one_pid() throws BundleException, IOException
{
// 1. Bundle registers service with pid1
@@ -533,4 +536,198 @@
TestCase.assertEquals( 1, tester21.numManagedServiceUpdatedCalls );
}
+
+ @Test
+ public void test_switch_static_binding() throws BundleException
+ {
+ // 1. create config with pid and locationA
+ // 2. update config with properties
+ final String pid = "test_switch_static_binding";
+ final String locationA = "test:location/A/" + pid;
+ final Configuration config = configure( pid, locationA, true );
+
+ // 3. register ManagedService ms1 with pid from said locationA
+ final Bundle bundleA = installBundle( pid, ManagedServiceTestActivator.class, locationA );
+ bundleA.start();
+ delay();
+
+ // ==> configuration supplied to the service ms1
+ final ManagedServiceTestActivator testerA1 = ManagedServiceTestActivator.INSTANCE;
+ TestCase.assertNotNull( testerA1.props );
+ TestCase.assertEquals( 1, testerA1.numManagedServiceUpdatedCalls );
+
+ // 4. register ManagedService ms2 with pid from locationB
+ final String locationB = "test:location/B/" + pid;
+ final Bundle bundleB = installBundle( pid, ManagedServiceTestActivator2.class, locationB );
+ bundleB.start();
+ delay();
+
+ // ==> configuration not supplied to service ms2
+ final ManagedServiceTestActivator2 testerB1 = ManagedServiceTestActivator2.INSTANCE;
+ TestCase.assertNull( testerB1.props );
+ TestCase.assertEquals( 0, testerB1.numManagedServiceUpdatedCalls );
+
+ // 5. Call Configuration.setBundleLocation( "locationB" )
+ config.setBundleLocation( locationB );
+ delay();
+
+ // ==> configuration is bound to locationB
+ TestCase.assertEquals( locationB, config.getBundleLocation() );
+
+ // ==> 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 );
+ }
+
+
+ @Test
+ public void test_switch_dynamic_binding() throws BundleException
+ {
+ // 1. create config with pid with null location
+ // 2. update config with properties
+ final String pid = "test_switch_dynamic_binding";
+ final String locationA = "test:location/A/" + pid;
+ final Configuration config = configure( pid, null, true );
+
+ // 3. register ManagedService ms1 with pid from locationA
+ final Bundle bundleA = installBundle( pid, ManagedServiceTestActivator.class, locationA );
+ bundleA.start();
+ delay();
+
+ // ==> configuration supplied to the service ms1
+ final ManagedServiceTestActivator testerA1 = ManagedServiceTestActivator.INSTANCE;
+ TestCase.assertNotNull( testerA1.props );
+ TestCase.assertEquals( 1, testerA1.numManagedServiceUpdatedCalls );
+
+ // ==> configuration is dynamically bound to locationA
+ TestCase.assertEquals( locationA, config.getBundleLocation() );
+
+ // 4. register ManagedService ms2 with pid from locationB
+ final String locationB = "test:location/B/" + pid;
+ final Bundle bundleB = installBundle( pid, ManagedServiceTestActivator2.class, locationB );
+ bundleB.start();
+ delay();
+
+ // ==> configuration not supplied to service ms2
+ final ManagedServiceTestActivator2 testerB1 = ManagedServiceTestActivator2.INSTANCE;
+ TestCase.assertNull( testerB1.props );
+ TestCase.assertEquals( 0, testerB1.numManagedServiceUpdatedCalls );
+
+ // 5. Call Configuration.setBundleLocation( "locationB" )
+ config.setBundleLocation( locationB );
+ delay();
+
+ // ==> configuration is bound to locationB
+ TestCase.assertEquals( locationB, config.getBundleLocation() );
+
+ // ==> 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 );
+ }
+
+
+ @Test
+ public void test_switch_static_binding_factory() throws BundleException
+ {
+ // 1. create config with pid and locationA
+ // 2. update config with properties
+ final String factoryPid = "test_switch_static_binding_factory";
+ final String locationA = "test:location/A/" + factoryPid;
+ final Configuration config = createFactoryConfiguration( factoryPid, locationA, true );
+ final String pid = config.getPid();
+
+ // 3. register ManagedService ms1 with pid from said locationA
+ final Bundle bundleA = installBundle( factoryPid, ManagedServiceFactoryTestActivator.class, locationA );
+ bundleA.start();
+ delay();
+
+ // ==> configuration supplied to the service ms1
+ final ManagedServiceFactoryTestActivator testerA1 = ManagedServiceFactoryTestActivator.INSTANCE;
+ TestCase.assertNotNull( testerA1.configs.get( pid ) );
+ TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
+
+ // 4. register ManagedService ms2 with pid from locationB
+ final String locationB = "test:location/B/" + factoryPid;
+ final Bundle bundleB = installBundle( factoryPid, ManagedServiceFactoryTestActivator2.class, locationB );
+ bundleB.start();
+ delay();
+
+ // ==> configuration not supplied to service ms2
+ final ManagedServiceFactoryTestActivator2 testerB1 = ManagedServiceFactoryTestActivator2.INSTANCE;
+ TestCase.assertNull( testerB1.configs.get( pid ));
+ TestCase.assertEquals( 0, testerB1.numManagedServiceFactoryUpdatedCalls );
+
+ // 5. Call Configuration.setBundleLocation( "locationB" )
+ config.setBundleLocation( locationB );
+ delay();
+
+ // ==> configuration is bound to locationB
+ TestCase.assertEquals( locationB, config.getBundleLocation() );
+
+ // ==> 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 );
+ }
+
+
+ @Test
+ public void test_switch_dynamic_binding_factory() throws BundleException
+ {
+ // 1. create config with pid and locationA
+ // 2. update config with properties
+ final String factoryPid = "test_switch_static_binding_factory";
+ final String locationA = "test:location/A/" + factoryPid;
+ final Configuration config = createFactoryConfiguration( factoryPid, null, true );
+ final String pid = config.getPid();
+
+ // 3. register ManagedService ms1 with pid from said locationA
+ final Bundle bundleA = installBundle( factoryPid, ManagedServiceFactoryTestActivator.class, locationA );
+ bundleA.start();
+ delay();
+
+ // ==> configuration supplied to the service ms1
+ final ManagedServiceFactoryTestActivator testerA1 = ManagedServiceFactoryTestActivator.INSTANCE;
+ TestCase.assertNotNull( testerA1.configs.get( pid ) );
+ TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
+
+ // 4. register ManagedService ms2 with pid from locationB
+ final String locationB = "test:location/B/" + factoryPid;
+ final Bundle bundleB = installBundle( factoryPid, ManagedServiceFactoryTestActivator2.class, locationB );
+ bundleB.start();
+ delay();
+
+ // ==> configuration not supplied to service ms2
+ final ManagedServiceFactoryTestActivator2 testerB1 = ManagedServiceFactoryTestActivator2.INSTANCE;
+ TestCase.assertNull( testerB1.configs.get( pid ));
+ TestCase.assertEquals( 0, testerB1.numManagedServiceFactoryUpdatedCalls );
+
+ // 5. Call Configuration.setBundleLocation( "locationB" )
+ config.setBundleLocation( locationB );
+ delay();
+
+ // ==> configuration is bound to locationB
+ TestCase.assertEquals( locationB, config.getBundleLocation() );
+
+ // ==> 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 );
+ }
}
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 f1002f9..28e1738 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
@@ -110,6 +110,13 @@
protected Bundle installBundle( final String pid, final Class<?> activatorClass ) throws BundleException
{
+ return installBundle( pid, activatorClass, activatorClass.getName() );
+ }
+
+
+ protected Bundle installBundle( final String pid, final Class<?> activatorClass, final String location )
+ throws BundleException
+ {
final String activatorClassName = activatorClass.getName();
final InputStream bundleStream = new MyTinyBundle().prepare(
withBnd().set( Constants.BUNDLE_SYMBOLICNAME, activatorClassName ).set( Constants.BUNDLE_VERSION, "0.0.11" )
@@ -119,7 +126,7 @@
try
{
- return bundleContext.installBundle( "test:" + activatorClassName, bundleStream );
+ return bundleContext.installBundle( location, bundleStream );
}
finally
{