FELIX-1488 Dynamic configuration bindings have to also be
persisted for this use case:
1. start managed service s1 of bundle b1
2. configuration is prodived and bound to b1
3. stop bundle b1
4. restart configuration admin service
==> configuration must still be bound to b1
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@804891 13f79535-47bb-0310-9956-ffa450edef68
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 d67b549..b8d6f4b 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
@@ -145,8 +145,18 @@
this.pid = ( String ) properties.remove( Constants.SERVICE_PID );
this.factoryPID = ( String ) properties.remove( ConfigurationAdmin.SERVICE_FACTORYPID );
+
+ // set bundle location from persistence and/or check for dynamic binding
this.bundleLocation = ( String ) properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
- this.staticallyBound = bundleLocation != null;
+ if ( bundleLocation != null )
+ {
+ this.staticallyBound = true;
+ }
+ else
+ {
+ this.staticallyBound = false;
+ this.bundleLocation = configurationManager.getDynamicBundleLocation( this.pid );
+ }
// set the properties internally
configureFromPersistence( properties );
@@ -289,7 +299,7 @@
/* (non-Javadoc)
* @see org.osgi.service.cm.Configuration#setBundleLocation(java.lang.String)
*/
- public void setBundleLocation( String bundleLocation, boolean staticBinding )
+ public void setBundleLocation( final String bundleLocation, final boolean staticBinding )
{
if ( !isDeleted() )
{
@@ -311,8 +321,11 @@
}
else if ( !staticallyBound )
{
- // dynamic binding, no need to store
+ // dynamic binding
this.bundleLocation = bundleLocation;
+
+ // keep the dynamic binding
+ this.configurationManager.setDynamicBundleLocation( this.getPid(), bundleLocation );
}
}
}
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 0644780..0315950 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
@@ -141,6 +141,19 @@
// have this always set to prevent NPE on bundle shutdown
private final Map configurations = new HashMap();
+ /**
+ * The map of dynamic configuration bindings. This maps the
+ * PID of the dynamically bound configuration or factory to its bundle
+ * location.
+ * <p>
+ * On bundle startup this map is loaded from persistence and validated
+ * against the locations of installed bundles: Entries pointing to bundle
+ * locations not currently installed are removed.
+ * <p>
+ * The map is written to persistence on each change.
+ */
+ private DynamicBindings dynamicBindings;
+
// the maximum log level when no LogService is available
private int logLevel = CM_LOG_LEVEL_DEFAULT;
@@ -193,6 +206,13 @@
props.put( Constants.SERVICE_VENDOR, "Apache Software Foundation" );
props.put( Constants.SERVICE_RANKING, new Integer( Integer.MIN_VALUE ) );
bundleContext.registerService( PersistenceManager.class.getName(), fpm, props );
+
+ // setup dynamic configuration bindings
+ dynamicBindings = new DynamicBindings( bundleContext, fpm );
+ }
+ catch ( IOException ioe )
+ {
+ log( LogService.LOG_ERROR, "Failure setting up dynamic configuration bindings", ioe );
}
catch ( IllegalArgumentException iae )
{
@@ -363,8 +383,37 @@
}
+ // ---------- ConfigurationAdminImpl support
- // ---------- ConfigurationAdminImpl support -------------------------------
+ void setDynamicBundleLocation( final String pid, final String location )
+ {
+ if ( dynamicBindings != null )
+ {
+ try
+ {
+ dynamicBindings.putLocation( pid, location );
+ }
+ catch ( IOException ioe )
+ {
+ log( LogService.LOG_ERROR, "Failed storing dynamic configuration binding for " + pid + " to "
+ + location, ioe );
+ }
+ }
+ }
+
+
+ String getDynamicBundleLocation( final String pid )
+ {
+ if ( dynamicBindings != null )
+ {
+ return dynamicBindings.getLocation( pid );
+ }
+
+ return null;
+ }
+
+
+ // ---------- ConfigurationAdminImpl support
/*
* (non-Javadoc)
@@ -691,7 +740,7 @@
{
if ( Factory.exists( pmList[i], factoryPid ) )
{
- factory = Factory.load( pmList[i], factoryPid );
+ factory = Factory.load( this, pmList[i], factoryPid );
cacheFactory( factory );
return factory;
}
@@ -704,7 +753,7 @@
Factory createFactory( String factoryPid )
{
- Factory factory = new Factory( getPersistenceManagers()[0], factoryPid );
+ Factory factory = new Factory( this, getPersistenceManagers()[0], factoryPid );
cacheFactory( factory );
return factory;
}
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/DynamicBindings.java b/configadmin/src/main/java/org/apache/felix/cm/impl/DynamicBindings.java
new file mode 100644
index 0000000..e242e9d
--- /dev/null
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/DynamicBindings.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.cm.impl;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import org.apache.felix.cm.PersistenceManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+
+class DynamicBindings
+{
+
+ static final String BINDINGS_FILE_NAME = "org_apache_felix_cm_impl_DynamicBindings";
+
+ private final PersistenceManager persistenceManager;
+
+ private final Dictionary bindings;
+
+
+ DynamicBindings( BundleContext bundleContext, PersistenceManager persistenceManager ) throws IOException
+ {
+ this.persistenceManager = persistenceManager;
+
+ if ( persistenceManager.exists( BINDINGS_FILE_NAME ) )
+ {
+ this.bindings = persistenceManager.load( BINDINGS_FILE_NAME );
+
+ // get locations of installed bundles to validate the bindings
+ final HashSet locations = new HashSet();
+ final Bundle[] bundles = bundleContext.getBundles();
+ for ( int i = 0; i < bundles.length; i++ )
+ {
+ locations.add( bundles[i].getLocation() );
+ }
+
+ // collect pids whose location is not installed any more
+ ArrayList removedKeys = new ArrayList();
+ for ( Enumeration ke = bindings.keys(); ke.hasMoreElements(); )
+ {
+ final String pid = ( String ) ke.nextElement();
+ final String location = ( String ) bindings.get( pid );
+ if ( !locations.contains( location ) )
+ {
+ removedKeys.add( pid );
+ }
+ }
+
+ // if some bindings had to be removed, store the mapping again
+ if ( removedKeys.size() > 0 )
+ {
+ // remove invalid mappings
+ for ( Iterator rki = removedKeys.iterator(); rki.hasNext(); )
+ {
+ bindings.remove( rki.next() );
+ }
+
+ // store the modified map
+ persistenceManager.store( BINDINGS_FILE_NAME, bindings );
+ }
+ }
+ else
+ {
+ this.bindings = new Hashtable();
+ }
+
+ }
+
+
+ String getLocation( final String pid )
+ {
+ synchronized ( this )
+ {
+ return ( String ) this.bindings.get( pid );
+ }
+ }
+
+
+ void putLocation( final String pid, final String location ) throws IOException
+ {
+ synchronized ( this )
+ {
+ if ( location == null )
+ {
+ this.bindings.remove( pid );
+ }
+ else
+ {
+ this.bindings.put( pid, location );
+ }
+
+ this.persistenceManager.store( BINDINGS_FILE_NAME, bindings );
+ }
+ }
+}
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 366090d..9bff795 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
@@ -27,6 +27,7 @@
import org.apache.felix.cm.PersistenceManager;
import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.log.LogService;
/**
@@ -40,20 +41,26 @@
public static final String FACTORY_PID_LIST = "factory.pidList";
+ /**
+ * The {@link ConfigurationManager configuration manager} instance which
+ * caused this configuration object to be created.
+ */
+ private final ConfigurationManager configurationManager;
+
// the persistence manager storing this factory mapping
- private PersistenceManager persistenceManager;
+ private final PersistenceManager persistenceManager;
// the factory PID of this factory
- private String factoryPid;
+ private final String factoryPid;
// the bundle location to which factory PID mapping is bound
- private String bundleLocation;
+ private volatile String bundleLocation;
// whether the factory is statically bound to a bundle or not
- private boolean staticallyBound;
+ private volatile boolean staticallyBound;
// the set of configuration PIDs belonging to this factory
- private Set pids;
+ private final Set pids;
static boolean exists( PersistenceManager persistenceManager, String factoryPid )
@@ -62,23 +69,11 @@
}
- static Factory load( PersistenceManager persistenceManager, String factoryPid ) throws IOException
+ static Factory load( ConfigurationManager configurationManager, PersistenceManager persistenceManager,
+ String factoryPid ) throws IOException
{
Dictionary dict = persistenceManager.load( factoryPidToIdentifier( factoryPid ) );
- return new Factory( persistenceManager, factoryPid, dict );
- }
-
-
- static Factory getFactory( PersistenceManager persistenceManager, Dictionary props )
- {
- // ignore non-Configuration dictionaries
- String factoryPid = ( String ) props.get( Factory.FACTORY_PID );
- if ( factoryPid == null )
- {
- return null;
- }
-
- return new Factory( persistenceManager, factoryPid, props );
+ return new Factory( configurationManager, persistenceManager, factoryPid, dict );
}
@@ -88,8 +83,9 @@
}
- Factory( PersistenceManager persistenceManager, String factoryPid )
+ Factory( ConfigurationManager configurationManager, PersistenceManager persistenceManager, String factoryPid )
{
+ this.configurationManager = configurationManager;
this.persistenceManager = persistenceManager;
this.factoryPid = factoryPid;
this.pids = new HashSet();
@@ -98,13 +94,21 @@
}
- Factory( PersistenceManager persistenceManager, String factoryPid, Dictionary props )
+ Factory( ConfigurationManager configurationManager, PersistenceManager persistenceManager, String factoryPid, Dictionary props )
{
- this( persistenceManager, factoryPid );
+ this( configurationManager, persistenceManager, factoryPid );
- // set bundle location
+ // set bundle location from persistence and/or check for dynamic binding
this.bundleLocation = ( String ) props.get( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
- this.staticallyBound = this.bundleLocation != null;
+ if ( bundleLocation != null )
+ {
+ this.staticallyBound = true;
+ }
+ else
+ {
+ this.staticallyBound = false;
+ this.bundleLocation = configurationManager.getDynamicBundleLocation( getFactoryPid() );
+ }
// set pids
String[] pidList = ( String[] ) props.get( FACTORY_PID_LIST );
@@ -148,7 +152,11 @@
}
else if ( !this.staticallyBound )
{
+ // dynamic binding
this.bundleLocation = bundleLocation;
+
+ // keep the dynamic binding
+ this.configurationManager.setDynamicBundleLocation( this.getFactoryPid(), bundleLocation );
}
}
@@ -206,9 +214,7 @@
}
catch ( IOException ioe )
{
- // should actually log this problem
- // configurationManager.log( LogService.LOG_ERROR, "Persisting new
- // bundle location failed", ioe );
+ configurationManager.log( LogService.LOG_ERROR, "Persisting new bundle location failed", ioe );
}
}
}
diff --git a/configadmin/src/test/java/org/apache/felix/cm/MockBundle.java b/configadmin/src/test/java/org/apache/felix/cm/MockBundle.java
new file mode 100644
index 0000000..d1ef4e7
--- /dev/null
+++ b/configadmin/src/test/java/org/apache/felix/cm/MockBundle.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.cm;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+
+
+public class MockBundle implements Bundle
+{
+
+ private final BundleContext context;
+ private final String location;
+
+
+ public MockBundle( BundleContext context, String location )
+ {
+ this.context = context;
+ this.location = location;
+ }
+
+
+ public Enumeration findEntries( String arg0, String arg1, boolean arg2 )
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+ public BundleContext getBundleContext()
+ {
+ return context;
+ }
+
+
+ public long getBundleId()
+ {
+ return 0;
+ }
+
+
+ public URL getEntry( String arg0 )
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+ public Enumeration getEntryPaths( String arg0 )
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+ public Dictionary getHeaders()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+ public Dictionary getHeaders( String arg0 )
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+ public long getLastModified()
+ {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+
+ public String getLocation()
+ {
+ return location;
+ }
+
+
+ public ServiceReference[] getRegisteredServices()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+ public URL getResource( String arg0 )
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+ public Enumeration getResources( String arg0 ) throws IOException
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+ public ServiceReference[] getServicesInUse()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+ public int getState()
+ {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+
+ public String getSymbolicName()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+ public boolean hasPermission( Object arg0 )
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+
+ public Class loadClass( String arg0 ) throws ClassNotFoundException
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+ public void start() throws BundleException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ public void stop() throws BundleException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ public void uninstall() throws BundleException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ public void update() throws BundleException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ public void update( InputStream arg0 ) throws BundleException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/configadmin/src/test/java/org/apache/felix/cm/MockBundleContext.java b/configadmin/src/test/java/org/apache/felix/cm/MockBundleContext.java
index c0aaae2..8cb6353 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/MockBundleContext.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/MockBundleContext.java
@@ -1,4 +1,4 @@
-/*
+/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -155,7 +155,7 @@
*/
public Bundle[] getBundles()
{
- return null;
+ return new Bundle[0];
}
diff --git a/configadmin/src/test/java/org/apache/felix/cm/impl/DynamicBindingsTest.java b/configadmin/src/test/java/org/apache/felix/cm/impl/DynamicBindingsTest.java
new file mode 100644
index 0000000..b3a3244
--- /dev/null
+++ b/configadmin/src/test/java/org/apache/felix/cm/impl/DynamicBindingsTest.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.cm.impl;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.Dictionary;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.cm.MockBundle;
+import org.apache.felix.cm.MockBundleContext;
+import org.apache.felix.cm.file.FilePersistenceManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+
+public class DynamicBindingsTest extends TestCase
+{
+
+ private File configLocation;
+
+ private File bindingsFile;
+
+ private FilePersistenceManager persistenceManager;
+
+ private static final String PID1 = "test.pid.1";
+
+ private static final String PID2 = "test.pid.2";
+
+ private static final String LOCATION1 = "test://location.1";
+
+ private static final String LOCATION2 = "test://location.2";
+
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ configLocation = new File( "target/config." + System.currentTimeMillis() );
+ persistenceManager = new FilePersistenceManager( configLocation.getAbsolutePath() );
+
+ bindingsFile = new File( configLocation, DynamicBindings.BINDINGS_FILE_NAME + ".config" );
+ }
+
+
+ protected void tearDown() throws Exception
+ {
+ bindingsFile.delete();
+ configLocation.delete();
+
+ super.tearDown();
+ }
+
+
+ public void test_no_bindings() throws IOException
+ {
+
+ // ensure there is no file
+ bindingsFile.delete();
+
+ final BundleContext ctx = new MockBundleContext();
+ final DynamicBindings dm = new DynamicBindings( ctx, persistenceManager );
+ final Dictionary bindings = getBindings( dm );
+
+ assertNotNull( bindings );
+ assertTrue( bindings.isEmpty() );
+ }
+
+
+ public void test_store_bindings() throws IOException
+ {
+ // ensure there is no file
+ bindingsFile.delete();
+
+ final BundleContext ctx = new MockBundleContext();
+ final DynamicBindings dm = new DynamicBindings( ctx, persistenceManager );
+
+ dm.putLocation( PID1, LOCATION1 );
+ assertEquals( LOCATION1, dm.getLocation( PID1 ) );
+
+ assertTrue( bindingsFile.exists() );
+
+ final Dictionary bindings = persistenceManager.load( DynamicBindings.BINDINGS_FILE_NAME );
+ assertNotNull( bindings );
+ assertEquals( 1, bindings.size() );
+ assertEquals( LOCATION1, bindings.get( PID1 ) );
+ }
+
+
+ public void test_store_and_load_bindings() throws IOException
+ {
+ // ensure there is no file
+ bindingsFile.delete();
+
+ // preset bindings
+ final DynamicBindings dm0 = new DynamicBindings( new MockBundleContext(), persistenceManager );
+ dm0.putLocation( PID1, LOCATION1 );
+
+ // check bindings
+ final BundleContext ctx = new DMTestMockBundleContext();
+ final DynamicBindings dm = new DynamicBindings( ctx, persistenceManager );
+
+ // API check
+ assertEquals( LOCATION1, dm.getLocation( PID1 ) );
+
+ // low level check
+ final Dictionary bindings = getBindings( dm );
+ assertNotNull( bindings );
+ assertEquals( 1, bindings.size() );
+ assertEquals( LOCATION1, bindings.get( PID1 ) );
+ }
+
+
+ public void test_store_and_load_bindings_with_cleanup() throws IOException
+ {
+ // ensure there is no file
+ bindingsFile.delete();
+
+ // preset bindings
+ final DynamicBindings dm0 = new DynamicBindings( new MockBundleContext(), persistenceManager );
+ dm0.putLocation( PID1, LOCATION1 );
+
+ // check bindings
+ final DynamicBindings dm = new DynamicBindings( new MockBundleContext(), persistenceManager );
+
+ // API check
+ assertNull( dm.getLocation( PID1 ) );
+
+ // low level check
+ final Dictionary bindings = getBindings( dm );
+ assertNotNull( bindings );
+ assertTrue( bindings.isEmpty() );
+ }
+
+
+ private static Dictionary getBindings( DynamicBindings dm )
+ {
+ try
+ {
+ final Field bindings = dm.getClass().getDeclaredField( "bindings" );
+ bindings.setAccessible( true );
+ return ( Dictionary ) bindings.get( dm );
+ }
+ catch ( Throwable t )
+ {
+ fail( "Cannot get bindings field: " + t );
+ return null;
+ }
+ }
+
+ private static class DMTestMockBundleContext extends MockBundleContext
+ {
+ public Bundle[] getBundles()
+ {
+ return new Bundle[]
+ { new MockBundle( this, LOCATION1 ) };
+ }
+ }
+}
diff --git a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTest.java b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTest.java
index ab77a1b..9eff377 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTest.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTest.java
@@ -535,6 +535,95 @@
}
+ @Test
+ public void test_static_binding_and_unbinding() throws BundleException
+ {
+ final String pid = "test_static_binding_and_unbinding";
+ final String location = bundleContext.getBundle().getLocation();
+
+ // create and statically bind the configuration
+ configure( pid );
+ final Configuration config = getConfiguration( pid );
+ TestCase.assertEquals( pid, config.getPid() );
+ TestCase.assertNull( config.getBundleLocation() );
+
+ // bind the configuration
+ config.setBundleLocation( location );
+ TestCase.assertEquals( location, config.getBundleLocation() );
+
+ // restart CM bundle
+ final Bundle cmBundle = getCmBundle();
+ cmBundle.stop();
+ delay();
+ cmBundle.start();
+
+ // assert configuration still bound
+ final Configuration configAfterRestart = getConfiguration( pid );
+ TestCase.assertEquals( pid, configAfterRestart.getPid() );
+ TestCase.assertEquals( location, configAfterRestart.getBundleLocation() );
+
+ // unbind the configuration
+ configAfterRestart.setBundleLocation( null );
+ TestCase.assertNull( configAfterRestart.getBundleLocation() );
+
+ // restart CM bundle
+ cmBundle.stop();
+ delay();
+ cmBundle.start();
+
+ // assert configuration unbound
+ final Configuration configUnboundAfterRestart = getConfiguration( pid );
+ TestCase.assertEquals( pid, configUnboundAfterRestart.getPid() );
+ TestCase.assertNull( configUnboundAfterRestart.getBundleLocation() );
+ }
+
+
+ @Test
+ public void test_dynamic_binding_and_unbinding() throws BundleException
+ {
+ final String pid = "test_dynamic_binding_and_unbinding";
+
+ // create and statically bind the configuration
+ configure( pid );
+ final Configuration config = getConfiguration( pid );
+ TestCase.assertEquals( pid, config.getPid() );
+ TestCase.assertNull( config.getBundleLocation() );
+
+ // dynamically bind the configuration
+ bundle = installBundle( pid );
+ final String location = bundle.getLocation();
+ bundle.start();
+ delay();
+ TestCase.assertEquals( location, config.getBundleLocation() );
+
+ // restart CM bundle
+ final Bundle cmBundle = getCmBundle();
+ cmBundle.stop();
+ delay();
+ cmBundle.start();
+
+ // assert configuration still bound
+ final Configuration configAfterRestart = getConfiguration( pid );
+ TestCase.assertEquals( pid, configAfterRestart.getPid() );
+ TestCase.assertEquals( location, configAfterRestart.getBundleLocation() );
+
+ // stop bundle (configuration remains bound !!)
+ bundle.stop();
+ delay();
+ TestCase.assertEquals( location, configAfterRestart.getBundleLocation() );
+
+ // restart CM bundle
+ cmBundle.stop();
+ delay();
+ cmBundle.start();
+
+ // assert configuration still bound
+ final Configuration configBoundAfterRestart = getConfiguration( pid );
+ TestCase.assertEquals( pid, configBoundAfterRestart.getPid() );
+ TestCase.assertEquals( location, configBoundAfterRestart.getBundleLocation() );
+ }
+
+
/*
@Test
public void test_() throws BundleException