moved new dm4 from sandbox to trunk.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1663056 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AbstractServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AbstractServiceDependencyTest.java
new file mode 100644
index 0000000..fb0bbe0
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AbstractServiceDependencyTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.dm.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AbstractServiceDependencyTest extends TestBase {
+   public void testAbstractClassDependency() {
+       DependencyManager m = getDM();
+       // helper class that ensures certain steps get executed in sequence
+       Ensure e = new Ensure();
+       // create a service provider and consumer
+       Component sp = m.createComponent()
+           .setInterface(ServiceAbstract.class.getName(), null)
+           .setImplementation(new ServiceProvider(e))
+           ;
+       Component sc = m.createComponent()
+           .setImplementation(new ServiceConsumer(e))
+           .add(m.createServiceDependency()
+               .setService(ServiceAbstract.class)
+               .setRequired(true)
+               .setCallbacks("bind", "unbind")
+               );
+       m.add(sp);
+       m.add(sc);
+       m.remove(sp);
+       // ensure we executed all steps inside the component instance
+       e.step(8);
+       m.clear();
+   }
+
+   static abstract class ServiceAbstract {
+       public abstract void invoke();
+   }
+
+   static class ServiceProvider extends ServiceAbstract {
+       private final Ensure m_ensure;
+       public ServiceProvider(Ensure e) {
+           m_ensure = e;
+       }
+
+       public void start() {
+           m_ensure.step(1);
+       }
+
+       public void invoke() {
+           m_ensure.step(4);
+       }
+
+       public void stop() {
+           m_ensure.step(7);
+       }
+   }
+
+   static class ServiceConsumer {
+       private volatile ServiceAbstract m_service;
+       private final Ensure m_ensure;
+
+       public ServiceConsumer(Ensure e) {
+           m_ensure = e;
+       }
+
+       public void bind(ServiceAbstract service) {
+           m_ensure.step(2);
+           m_service = service;
+       }
+
+       public void start() {
+           m_ensure.step(3);
+           m_service.invoke();
+       }
+
+       public void stop() {
+           m_ensure.step(5);
+       }
+
+       public void unbind(ServiceAbstract service) {
+           System.out.println("UNBINDDDDDDDDDDDDDDDDDDDDDDDDDDD");
+           Assert.assertEquals(m_service, service);
+           m_ensure.step(6);
+       }
+   }
+}
\ No newline at end of file
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterAndConsumerTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterAndConsumerTest.java
new file mode 100644
index 0000000..93949ee
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterAndConsumerTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.dm.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterAndConsumerTest extends TestBase {
+    
+    public void testServiceWithAdapterAndConsumer() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+
+        Component provider = m.createComponent()
+            .setInterface(OriginalService.class.getName(), null)
+            .setImplementation(new ServiceProvider(e));
+
+        Component consumer = m.createComponent()
+            .setImplementation(new ServiceConsumer(e))
+            .add(m.createServiceDependency()
+                .setService(AdaptedService.class)
+                .setRequired(true)
+            );
+
+        Component adapter = m.createAdapterService(OriginalService.class, null)
+            .setInterface(AdaptedService.class.getName(), null)
+            .setImplementation(ServiceAdapter.class);
+        
+        // add the provider and the adapter
+        m.add(provider);
+        m.add(adapter);
+        // add a consumer that will invoke the adapter
+        // which will in turn invoke the original provider
+        m.add(consumer);
+        // now validate that both have been invoked in the right order
+        e.waitForStep(2, 5000);
+        // remove the provider again
+        m.remove(provider);
+        // ensure that the consumer is stopped
+        e.waitForStep(3, 5000);
+        // remove adapter and consumer
+        m.remove(adapter);
+        m.remove(consumer);
+    }
+
+    static interface OriginalService {
+        public void invoke();
+    }
+    
+    static interface AdaptedService {
+        public void invoke();
+    }
+    
+    static class ServiceProvider implements OriginalService {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke() {
+            m_ensure.step(2);
+        }
+    }
+    
+    public static class ServiceAdapter implements AdaptedService {
+        private volatile OriginalService m_originalService;
+        
+        public void start() { System.out.println("start"); }
+        public void stop() { System.out.println("stop"); }
+        public void invoke() {
+            m_originalService.invoke();
+        }
+    }
+
+    static class ServiceConsumer {
+        private volatile AdaptedService m_service;
+        private final Ensure m_ensure;
+        
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        public void start() {
+            m_ensure.step(1);
+            m_service.invoke();
+        }
+        public void stop() {
+            m_ensure.step(3);
+        }
+    }
+}
+
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithCallbackInstanceTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithCallbackInstanceTest.java
new file mode 100644
index 0000000..a7073f5
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithCallbackInstanceTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterWithCallbackInstanceTest extends TestBase {
+    
+    public void testServiceWithAdapterAndConsumer() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+
+        ServiceProvider serviceProvider = new ServiceProvider(e);
+        Component provider = m.createComponent()
+            .setInterface(OriginalService.class.getName(), null)
+            .setImplementation(serviceProvider);
+
+        Component consumer = m.createComponent()
+            .setImplementation(new ServiceConsumer(e))
+            .add(m.createServiceDependency()
+                .setService(AdaptedService.class)
+                .setRequired(true)
+            );
+
+        ServiceAdapterCallbackInstance callbackInstance = new ServiceAdapterCallbackInstance(e);
+        Component adapter = m.createAdapterService(OriginalService.class, null, "m_originalService", 
+                                                   callbackInstance, "set", "changed","unset", null, true)
+            .setInterface(AdaptedService.class.getName(), null)
+            .setImplementation(new ServiceAdapter(e));
+        
+        // add the provider and the adapter
+        m.add(provider);
+        m.add(adapter);
+        // Checks if the callbackInstances is called, and if the adapter start method is called
+        e.waitForStep(2, 5000);
+        
+        // add a consumer that will invoke the adapter
+        // which will in turn invoke the original provider
+        m.add(consumer);
+        // now validate that both have been invoked in the right order
+        e.waitForStep(4, 5000);
+        
+        // change the service properties of the provider, and check that the adapter callback instance is changed.
+        serviceProvider.changeServiceProperties();
+        e.waitForStep(5, 5000);
+        
+        // remove the provider
+        m.remove(provider);
+        // ensure that the consumer is stopped, the adapter callback is called in its unset method, and the adapter is stopped.
+        e.waitForStep(8, 5000);
+        // remove adapter and consumer
+        m.remove(adapter);
+        m.remove(consumer);
+    }
+
+    static interface OriginalService {
+        public void invoke();
+    }
+    
+    static interface AdaptedService {
+        public void invoke();
+    }
+    
+    static class ServiceProvider implements OriginalService {
+        private final Ensure m_ensure;
+        private volatile ServiceRegistration m_registration; // auto injected when started.
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void changeServiceProperties() {
+            Hashtable<String, String> props = new Hashtable<>();
+            props.put("foo", "bar");
+            m_registration.setProperties(props);
+        }
+        public void invoke() {
+            m_ensure.step(4);
+        }
+    }
+    
+    public static class ServiceAdapter implements AdaptedService {
+        private volatile OriginalService m_originalService;
+        private final Ensure m_ensure;
+        
+        public ServiceAdapter(Ensure e) {
+            m_ensure = e;
+        }
+
+        public void start() { m_ensure.step(2); }
+        public void stop() { m_ensure.step(7); }
+        public void invoke() {
+            m_originalService.invoke();
+        }
+    }
+
+    public static class ServiceAdapterCallbackInstance {
+        private final Ensure m_ensure;
+        public ServiceAdapterCallbackInstance(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void set(OriginalService m_originalService) {
+            m_ensure.step(1);
+        }
+        
+        public void changed(Map<String, String> props, OriginalService m_originalService) {   
+            Assert.assertEquals("bar", props.get("foo"));
+            m_ensure.step(5);
+        }
+        
+        public void unset(Map<String, String> props, OriginalService m_originalService) {            
+            m_ensure.step(8);
+        }
+    }
+
+    static class ServiceConsumer {
+        private volatile AdaptedService m_service;
+        private final Ensure m_ensure;
+        
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        public void start() {
+            m_ensure.step(3);
+            m_service.invoke();
+        }
+        public void stop() {
+            m_ensure.step(6);
+        }
+    }
+}
+
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithConfigurationAndMetaType.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithConfigurationAndMetaType.java
new file mode 100644
index 0000000..68eedd6
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithConfigurationAndMetaType.java
@@ -0,0 +1,183 @@
+/*
+ * 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.dm.itest.api;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.metatype.AttributeDefinition;
+import org.osgi.service.metatype.MetaTypeInformation;
+import org.osgi.service.metatype.MetaTypeService;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+/**
+ * Tests an Adapter which adapts A To B interface.
+ * And the Adapter also depends on a Configuration Dependency with MetaType support.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterWithConfigurationAndMetaType extends TestBase {
+    final static String PID = "AdapterWithConfigurationAndMetaType";
+    final static String PID_HEADING = "English Dictionary";
+    final static String PID_DESC = "Configuration for the EnglishDictionary Service";
+    final static String WORDS_HEADING = "English words";
+    final static String WORDS_DESC = "Declare here some valid English words";
+    final static String WORDS_PROPERTY = "words";
+    
+    public void testAdapterWithConfigurationDependencyAndMetaType() {
+        DependencyManager m = getDM();
+        Ensure e = new Ensure();
+        
+        m.add(m.createAdapterService(A.class, null)
+            .setInterface(B.class.getName(), null)
+            .setImplementation(new BImpl(e))
+            .add(m.createConfigurationDependency()
+                .setPid(PID)
+                .setHeading(PID_HEADING)
+                .setDescription(PID_DESC)
+                .add(m.createPropertyMetaData()
+                    .setCardinality(Integer.MAX_VALUE)
+                    .setType(String.class)
+                    .setHeading(WORDS_HEADING)
+                    .setDescription(WORDS_DESC)
+                    .setDefaults(new String[] {"hello", "world"})
+                    .setId(WORDS_PROPERTY))));
+  
+        m.add(m.createComponent()
+            .setInterface(A.class.getName(), null)
+            .setImplementation(new AImpl()));
+      
+        Component configurator = m.createComponent()
+            .setImplementation(new Configurator(e))
+            .add(m.createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true))
+            .add(m.createServiceDependency().setService(MetaTypeService.class).setRequired(true));
+        m.add(configurator);
+        
+        // Ensures that all components are started
+        e.waitForStep(4, 5000);
+        
+        // now stop configurator, and ensure that all components have been stopped
+        m.remove(configurator);
+        e.waitForStep(7, 5000);
+        m.clear();
+    }
+    
+    public interface A {
+    }
+
+    public interface B {
+    }
+
+    public class AImpl implements A {
+    }
+    
+    public class Configurator {
+        volatile MetaTypeService m_metaType;
+        volatile ConfigurationAdmin m_cm;
+        volatile BundleContext m_ctx;
+        final Ensure m_ensure;
+        Configuration m_conf;
+
+        Configurator(Ensure ensure) {
+            m_ensure = ensure;
+        }
+
+        void start() {
+            m_ensure.step(1);
+            checkMetaTypeAndConfigure();
+        }        
+
+        void stop() {
+            m_ensure.step(5);
+            if (m_conf != null) {
+                try {
+                    m_ensure.step(6);
+                    m_conf.delete();
+                }
+                catch (IOException e) {
+                    m_ensure.throwable(e);
+                }
+            }
+        }
+        
+        void checkMetaTypeAndConfigure() {
+            MetaTypeInformation info = m_metaType.getMetaTypeInformation(m_ctx.getBundle());
+            Assert.assertNotNull(info);
+            Assert.assertEquals(PID, info.getPids()[0]);
+            ObjectClassDefinition ocd = info.getObjectClassDefinition(PID, null);
+            Assert.assertNotNull(ocd);
+            Assert.assertEquals(PID_HEADING, ocd.getName());
+            Assert.assertEquals(PID_DESC, ocd.getDescription());
+            AttributeDefinition[] defs = ocd.getAttributeDefinitions(ObjectClassDefinition.ALL);
+            Assert.assertNotNull(defs);
+            Assert.assertEquals(1, defs.length);
+            Assert.assertEquals(WORDS_HEADING, defs[0].getName());
+            Assert.assertEquals(WORDS_DESC, defs[0].getDescription());
+            Assert.assertEquals(WORDS_PROPERTY, defs[0].getID());
+            m_ensure.step(2);
+            
+            try {
+                m_conf = m_cm.getConfiguration(PID, null);
+                Hashtable<String, String> props = new Hashtable<>();
+                props.put("foo", "bar");
+                m_conf.update(props);                
+            } catch (Throwable t) {
+                m_ensure.throwable(t);
+            }
+        }
+    }
+
+    public class BImpl implements B, A {
+        final Ensure m_ensure;
+        volatile A m_a;
+        Dictionary<String, String> m_conf;
+
+        public BImpl(Ensure e) {
+            m_ensure = e;
+        }
+
+        public void updated(Dictionary<String, String> conf) {
+            if (conf != null) {
+                m_ensure.step(3);
+                m_conf = conf;
+            }
+        }
+
+        public void start() {
+            Assert.assertNotNull(m_a);
+            Assert.assertNotNull(m_conf);
+            Assert.assertEquals("bar", m_conf.get("foo"));
+            m_ensure.step(4);
+        }
+        
+        public void stop() {
+            m_ensure.step(7);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithExtraDependenciesTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithExtraDependenciesTest.java
new file mode 100644
index 0000000..a0c6fb1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithExtraDependenciesTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.dm.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterWithExtraDependenciesTest extends TestBase {
+    public void testAdapterWithExtraDependenciesAndCallbacks() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        
+        // create a service adapter that adapts to services S1 and has an optional dependency on services S2
+        Component sa = m.createAdapterService(S1.class, null)
+            .setImplementation(SA.class)
+            .add(m.createServiceDependency().setService(S2.class).setCallbacks("add", "remove"));
+        m.add(sa);
+        
+        // create a service S1, which triggers the creation of the first adapter instance (A1)
+        Component s1 = m.createComponent().setInterface(S1.class.getName(), null).setImplementation(new S1Impl());
+        m.add(s1);
+        
+        // create a service S2, which will be added to A1
+        Component s2 = m.createComponent().setInterface(S2.class.getName(), null).setImplementation(new S2Impl(e));
+        m.add(s2);
+        
+        // create a second service S1, which triggers the creation of the second adapter instance (A2)
+        Component s1b = m.createComponent().setInterface(S1.class.getName(), null).setImplementation(new S1Impl());
+        m.add(s1b);
+        
+        // observe that S2 is also added to A2
+        e.waitForStep(2, 5000);
+        
+        // remove S2 again
+        m.remove(s2);
+        
+        // make sure both adapters have their "remove" callbacks invoked
+        e.waitForStep(4, 5000);
+        
+        m.remove(s1);
+        m.remove(sa);
+        m.clear();
+    }
+    
+    static interface S1 {
+    }
+    static interface S2 {
+        public void invoke();
+    }
+    static class S1Impl implements S1 {
+    }
+    static class S2Impl implements S2 {
+
+        private final Ensure m_e;
+
+        public S2Impl(Ensure e) {
+            m_e = e;
+        }
+
+        public void invoke() {
+            m_e.step();
+        }
+    }
+    
+    public static class SA {
+        volatile S2 s2;
+        
+        public SA() {
+            System.out.println("Adapter created");
+        }
+        public void init() {
+            System.out.println("Adapter init " + s2);
+        }
+        public void add(S2 s) {
+            System.out.println("adding " + s);
+            s.invoke();
+        }
+        public void remove(S2 s) {
+            System.out.println("removing " + s);
+            s.invoke();
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithInstanceBoundDependencyParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithInstanceBoundDependencyParallelTest.java
new file mode 100644
index 0000000..4fe3edd
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithInstanceBoundDependencyParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.dm.itest.api;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterWithInstanceBoundDependencyParallelTest extends AdapterWithInstanceBoundDependencyTest {
+    public AdapterWithInstanceBoundDependencyParallelTest() {
+        setParallel();
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithInstanceBoundDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithInstanceBoundDependencyTest.java
new file mode 100644
index 0000000..77645a3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithInstanceBoundDependencyTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.dm.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterWithInstanceBoundDependencyTest extends TestBase {
+    public void testInstanceBoundDependency() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Component sp = m.createComponent()
+            .setInterface(ServiceInterface.class.getName(), null)
+            .setImplementation(new ServiceProvider(e));
+        Component sp2 = m.createComponent()
+            .setInterface(ServiceInterface2.class.getName(), null)
+            .setImplementation(new ServiceProvider2(e));
+        Component sc = m.createComponent()
+            .setImplementation(new ServiceConsumer(e))
+            .add(m.createServiceDependency()
+                .setService(ServiceInterface3.class)
+                .setRequired(true));
+        Component sa = m.createAdapterService(ServiceInterface.class, null)
+            .setInterface(ServiceInterface3.class.getName(), null)
+            .setImplementation(new ServiceAdapter(e));
+        m.add(sc);
+        m.add(sp);
+        m.add(sp2);
+        m.add(sa);
+        e.waitForStep(5, 15000);
+        // cleanup
+        m.remove(sa);
+        m.remove(sp2);
+        m.remove(sp);
+        m.remove(sc);
+        m.clear();
+        e.waitForStep(9, 5000); // make sure all components are stopped
+    }
+    
+    static interface ServiceInterface {
+        public void invoke();
+    }
+    
+    static interface ServiceInterface2 {
+        public void invoke();
+    }
+    
+    static interface ServiceInterface3 {
+        public void invoke();
+    }
+    
+    static class ServiceProvider2 implements ServiceInterface2 {
+        private final Ensure m_ensure;
+
+        public ServiceProvider2(Ensure ensure) {
+            m_ensure = ensure;
+        }
+
+        public void invoke() {
+            m_ensure.step(4);
+        }
+        
+        public void stop() {
+            m_ensure.step();
+        }
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke() {
+            m_ensure.step(5);
+        }
+        public void stop() {
+            m_ensure.step();
+        }
+    }
+    
+    static class ServiceAdapter implements ServiceInterface3 {
+        private Ensure m_ensure;
+        private volatile ServiceInterface m_originalService;
+        private volatile ServiceInterface2 m_injectedService;
+        private volatile Component m_service;
+        private volatile DependencyManager m_manager;
+        
+        public ServiceAdapter(Ensure e) {
+            m_ensure = e;
+        }
+        public void init() {
+            m_ensure.step(1);
+            m_service.add(m_manager.createServiceDependency().setRequired(true).setService(ServiceInterface2.class));
+        }
+        public void start() {
+            m_ensure.step(2);
+        }
+        public void invoke() {
+            m_ensure.step(3);
+            m_injectedService.invoke();
+            m_originalService.invoke();
+        }
+        
+        public void stop() {
+            m_ensure.step();
+        }
+    }
+
+    static class ServiceConsumer implements Runnable {
+        volatile ServiceInterface3 m_service;
+        final Ensure m_ensure;
+        
+        ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            Thread t = new Thread(this);
+            t.start();
+        }
+        
+        public void run() {
+            m_service.invoke();
+        }
+        public void stop() {
+            m_ensure.step();
+        }
+    }
+}
+
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithModifiedInstanceBoundDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithModifiedInstanceBoundDependencyTest.java
new file mode 100644
index 0000000..46ecf74
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithModifiedInstanceBoundDependencyTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * Test for FELIX-4334 issue.
+ * 
+ * Three components: A, B and C
+ * 
+ * - A provided with property foo=bar
+ * - B adapts A, B has no filters on A, and B.init() method adds an instance bound required dependency to C.
+ * - C depends on A(foo=bar)
+ * - Now someone modifies the service properties of A: foo=bar2
+ * - As a result of that, C becomes unavailable and is unbound from B.
+ * - Since B has an instance bound required dependency to C: B should not be destroyed: it should be called in B.stop(), B.remove(C), B.change(A, "foo=bar2))
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class AdapterWithModifiedInstanceBoundDependencyTest extends TestBase {
+    public static interface A {
+    }
+    
+    static class AImpl implements A {
+        final Ensure m_e;
+        AImpl(Ensure e) {
+            m_e = e;
+        }        
+    }
+    
+    public static interface C {
+    }
+
+    static class CImpl implements C {
+        volatile A m_a;
+    }
+    
+    public static interface B {
+    }
+    
+    static class BImpl implements B {
+        final Ensure m_e;
+        volatile A m_a;
+        volatile C m_c;
+        
+        BImpl(Ensure e) {
+            m_e = e;
+        }
+        
+        public void add(A a) {
+            m_e.step(1);
+        }
+
+        void init(Component c) {
+            m_e.step(2);
+            DependencyManager dm = c.getDependencyManager();
+            c.add(dm.createServiceDependency().setService(C.class).setRequired(true).setCallbacks("add", "remove"));
+        }      
+        
+        public void add(C c) {
+            m_e.step(3);
+        }
+        
+        public void start() {
+            m_e.step(4);            
+        }
+        
+        public void stop() { // C becomes unsatisfied when A properties are changed to foo=bar2
+            m_e.step(5);
+        }
+
+        public void remove(C c) {
+            m_e.step(6);
+        }
+
+        public void change(Map properties, A a) {
+            Assert.assertEquals("bar2", properties.get("foo"));
+            m_e.step(7);
+        }
+        
+        public void destroy() {
+            m_e.step(8);
+        }
+
+        public void remove(A a) {   
+            m_e.step(9);
+        }                    
+    }
+    
+    public void testAdapterWithChangedInstanceBoundDependency() {
+        DependencyManager m = getDM();
+        Ensure e = new Ensure();
+
+        Dictionary props = new Hashtable();
+        props.put("foo", "bar");
+        Component a = m.createComponent()
+                .setImplementation(new AImpl(e))
+                .setInterface(A.class.getName(), props);
+        
+        Component b = m.createAdapterService(A.class, null, "add", "change", "remove")
+                .setInterface(B.class.getName(), null)
+                .setImplementation(new BImpl(e));                
+        
+        Component c = m.createComponent()
+                .setImplementation(new CImpl())
+                .setInterface(C.class.getName(), null)
+                .add(m.createServiceDependency().setService(A.class, "(foo=bar)").setRequired(true));                     
+              
+        m.add(a);
+        m.add(c);
+        m.add(b);
+        
+        e.waitForStep(4, 5000);
+        
+        System.out.println("changing A props ...");
+        props = new Hashtable();
+        props.put("foo", "bar2");
+        a.setServiceProperties(props);
+        
+        e.waitForStep(7, 5000);                
+        
+        m.remove(c);
+        m.remove(a);
+        m.remove(b);
+        
+        e.waitForStep(9, 5000);                
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithPropagationTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithPropagationTest.java
new file mode 100644
index 0000000..29ed79e
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithPropagationTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * Checks if a service adapter propagates its service properties, if 
+ * the adapted service properties are changed:
+ * 
+ * S1Impl provides S
+ * S1Adapter adapts S1Impl(S) to S2
+ * S3 depends on S2
+ * 
+ * So, when S1Impl service properties are changed, S1Adapter shall propagate the changed properties to S3.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class AdapterWithPropagationTest extends TestBase {
+    public static interface S1 {}
+    
+    static class S1Impl implements S1 {
+        private Ensure m_ensure;
+        public S1Impl(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void start() {
+            m_ensure.step(1);
+        }
+    }
+    
+    public static interface S2 {}
+
+    static class S1Adapter implements S2 {
+        private Ensure m_ensure;
+        public S1Adapter(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void add(Map properties, S1 s1) {
+            Assert.assertTrue("v1".equals(properties.get("p1")));
+            Assert.assertTrue("v2overriden".equals(properties.get("p2")));
+            m_ensure.step(2);
+        }
+        
+        public void change(Map properties, S1 s1) {   
+            Assert.assertTrue("v1modified".equals(properties.get("p1")));
+            Assert.assertTrue("v2overriden".equals(properties.get("p2")));
+            m_ensure.step(4);
+        }
+    }
+
+    static class S3 {
+        private final Ensure m_ensure;
+
+        public S3(Ensure e) {
+            m_ensure = e;
+        }
+                        
+        public void add(Map properties, S2 s2) {
+            Assert.assertTrue("v1".equals(properties.get("p1")));
+            Assert.assertTrue("v2".equals(properties.get("p2"))); // s1 should not override adapter service properties
+            m_ensure.step(3);
+        }
+        
+        public void change(Map properties, S2 s2) {
+            Assert.assertTrue("v1modified".equals(properties.get("p1")));
+            Assert.assertTrue("v2".equals(properties.get("p2"))); // s1 should not override adapter service properties
+            m_ensure.step(5);
+        }
+    }
+    
+    public void testAdapterWithPropagation() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure(); 
+        
+        Dictionary s1Properties = new Hashtable();
+        s1Properties.put("p1", "v1");
+        s1Properties.put("p2", "v2overriden"); // should not override adapter
+        Component s1 = m.createComponent()
+                .setImplementation(new S1Impl(e))
+                .setInterface(S1.class.getName(), s1Properties);
+        
+        Dictionary s1AdapterProperties = new Hashtable();
+        s1AdapterProperties.put("p2", "v2");
+        Component s1Adapter = m.createAdapterService(S1.class, null, "add", "change", null)
+                .setInterface(S2.class.getName(), s1AdapterProperties)
+                .setImplementation(new S1Adapter(e));
+        
+        Component s3 = m.createComponent()
+                .setImplementation(new S3(e))
+                .add(m.createServiceDependency()
+                     .setService(S2.class)
+                     .setRequired(true)
+                     .setCallbacks("add", "change", null));
+                     
+              
+        m.add(s1);
+        m.add(s1Adapter);
+        m.add(s3);
+        
+        e.waitForStep(3, 5000);
+        
+        s1Properties = new Hashtable();
+        s1Properties.put("p1", "v1modified");
+        s1Properties.put("p2", "v2overriden");
+        s1.setServiceProperties(s1Properties);
+        
+        e.waitForStep(5, 5000);
+
+        m.clear();
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithoutPropagationTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithoutPropagationTest.java
new file mode 100644
index 0000000..d50bbd3
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AdapterWithoutPropagationTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.junit.Assert;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterWithoutPropagationTest extends TestBase {
+    
+    public void testAdapterNoPropagate() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+
+        // The provider has a "foo=bar" property
+        Hashtable<String, String> props = new Hashtable<>();
+        props.put("foo", "bar");
+        ServiceProvider serviceProvider = new ServiceProvider(e);
+        Component provider = m.createComponent()
+            .setInterface(OriginalService.class.getName(), props).setImplementation(serviceProvider);
+
+        // The Adapter will see the "foo=bar" property from the adaptee
+        Component adapter = m.createAdapterService(OriginalService.class, null, null,
+                                                   null, "set", "change", null, null, false)
+            .setInterface(AdaptedService.class.getName(), null)
+            .setImplementation(new ServiceAdapter(e));
+
+        // The consumer depends on the AdaptedService, but won't see foo=bar property from the adaptee 
+        Component consumer = m.createComponent()
+            .setImplementation(new ServiceConsumer(e))
+            .add(m.createServiceDependency()
+                .setService(AdaptedService.class)
+                .setRequired(true)
+                .setCallbacks("set", "change", null)
+            );
+        
+        // add the provider and the adapter
+        m.add(provider);
+        m.add(adapter);
+        // Checks if the adapter has been started and has seen the adaptee properties
+        e.waitForStep(1, 5000);
+        
+        // add a consumer that must not see the adaptee service properties
+        m.add(consumer);
+        e.waitForStep(2, 5000);
+        
+        // change the service properties of the provider, and check that the adapter callback instance is caled.
+        serviceProvider.changeServiceProperties();
+        e.waitForStep(3, 5000);
+        
+        // cleanup
+        m.clear();
+    }
+
+    static interface OriginalService {
+    }
+    
+    static interface AdaptedService {
+    }
+    
+    static class ServiceProvider implements OriginalService {
+        private volatile ServiceRegistration m_registration; // auto injected when started.
+        public ServiceProvider(Ensure e) {
+        }
+        public void changeServiceProperties() {
+            Hashtable<String, String> props = new Hashtable<>();
+            props.put("foo", "bar2");
+            m_registration.setProperties(props);
+        }
+    }
+    
+    public static class ServiceAdapter implements AdaptedService {
+        private final Ensure m_ensure;
+        
+        public ServiceAdapter(Ensure e) {
+            m_ensure = e;
+        }
+
+        public void set(OriginalService adaptee, Dictionary<String, String> props) {
+            Assert.assertEquals("bar", props.get("foo"));
+            m_ensure.step(1);
+        }
+        
+        void change(OriginalService adapted, Dictionary<String, String> props) {
+            Assert.assertEquals("bar2", props.get("foo"));
+            m_ensure.step(3);
+        }
+    }
+
+    static class ServiceConsumer {
+        @SuppressWarnings("unused")
+		private volatile AdaptedService m_service;
+        private final Ensure m_ensure;
+        
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        void set(AdaptedService adapted, Dictionary<String, String> props) {
+            Assert.assertNull(props.get("foo"));
+            m_ensure.step(2);
+        }
+        
+        void change(AdaptedService adapted, Dictionary<String, String> props) {
+            Assert.assertNull(props.get("foo"));
+            Assert.fail("Change callback should not be called");
+        }
+    }
+}
+
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectBaseTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectBaseTest.java
new file mode 100644
index 0000000..e4d2d73
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectBaseTest.java
@@ -0,0 +1,227 @@
+/*
+ * 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.dm.itest.api;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Properties;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.ServiceUtil;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "unused"})
+public class AspectBaseTest extends TestBase {    
+
+	public void testSingleAspect() {
+	    DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        
+        // create a service provider and consumer
+        ServiceProvider p = new ServiceProvider(e, "a");
+        ServiceConsumer c = new ServiceConsumer(e);
+        Hashtable<String, Object> props = new Hashtable<>();
+        props.put("name", "a");
+        Component sp = m.createComponent()
+            .setInterface(ServiceInterface.class.getName(), props)
+            .setImplementation(p);
+        Component sc = m.createComponent()
+            .setImplementation(c)
+            .add(m.createServiceDependency()
+                .setService(ServiceInterface.class)
+                .setRequired(true)
+                .setCallbacks("add", "remove")
+                .setAutoConfig("m_service")
+            );
+        Component sa = m.createAspectService(ServiceInterface.class, null, 20, null)
+            .setImplementation(ServiceAspect.class);
+        m.add(sc);
+        m.add(sp);
+        // after the provider was added, the consumer's add should have been invoked once
+        e.waitForStep(1, 2000);
+        Assert.assertEquals("a", c.invoke());
+        m.add(sa);
+        // after the aspect was added, the consumer should get and add for the aspect and a remove
+        // for the original service
+        e.waitForStep(3, 2000);
+        Assert.assertEquals("aa", c.invoke());
+        m.remove(sa);
+        // removing the aspect again should give a remove and add
+        e.waitForStep(5, 2000);
+        Assert.assertEquals("a", c.invoke());
+        m.remove(sp);
+        // finally removing the original service should give a remove
+        e.waitForStep(6, 2000);
+        m.remove(sc);
+        e.step(7);
+    }
+    
+    @SuppressWarnings("serial")
+    public void testSingleAspectThatAlreadyExisted() {
+        DependencyManager m = new DependencyManager(context);
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        
+        // create a service provider and consumer
+        ServiceProvider p = new ServiceProvider(e, "a");
+        ServiceConsumer c = new ServiceConsumer(e);
+        Component sp = m.createComponent().setImplementation(p).setInterface(ServiceInterface.class.getName(), new Hashtable() {{ put("name", "a"); }});
+        Component sc = m.createComponent().setImplementation(c).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", "remove").setAutoConfig("m_service"));
+        Component sa = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(ServiceAspect.class);
+        // we first add the aspect
+        m.add(sa);
+        // then the service provider
+        m.add(sp);
+        // finally the consumer
+        m.add(sc);
+
+        Assert.assertEquals("aa", c.invoke());
+        
+        // now the consumer's added should be invoked once, as the aspect is already available and should
+        // directly hide the original service
+        e.waitForStep(1, 2000);
+        e.step(2);
+
+        m.remove(sa);
+        // after removing the aspect, the consumer should get the original service back, so
+        // remove and add will be invoked
+        e.waitForStep(4, 2000);
+        
+        Assert.assertEquals("a", c.invoke());
+        
+        m.remove(sp);
+        // after removing the original service, the consumer's remove should be called once
+        e.waitForStep(5, 2000);
+        
+        m.remove(sc);
+        e.step(6);
+    }
+
+    @SuppressWarnings("serial")
+    public void testMultipleAspects() {
+        DependencyManager m = new DependencyManager(context);
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        
+        // create service providers and consumers
+        ServiceConsumer c = new ServiceConsumer(e);
+        Component sp = m.createComponent().setImplementation(new ServiceProvider(e, "a")).setInterface(ServiceInterface.class.getName(), new Hashtable() {{ put("name", "a"); }});
+        Component sp2 = m.createComponent().setImplementation(new ServiceProvider(e, "b")).setInterface(ServiceInterface.class.getName(), new Hashtable() {{ put("name", "b"); }});
+        Component sc = m.createComponent().setImplementation(c).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", "remove"));
+        Component sa = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(ServiceAspect.class);
+        Component sa2 = m.createAspectService(ServiceInterface.class, null, 10, null).setImplementation(ServiceAspect.class);
+        m.add(sp);
+        m.add(sp2);
+        m.add(sa);
+        m.add(sa2);
+        m.add(sc);
+        // the consumer will monitor progress, it should get it's add invoked twice, once for every
+        // (highest) aspect
+        e.waitForStep(2, 2000);
+        e.step(3);
+        
+        // now invoke all services the consumer collected
+        List<String> list = c.invokeAll();
+        // and make sure both of them are correctly invoked
+        Assert.assertTrue(list.size() == 2);
+        Assert.assertTrue(list.contains("aaa"));
+        Assert.assertTrue(list.contains("bbb"));
+        
+        m.remove(sc);
+        // removing the consumer now should get its removed method invoked twice
+        e.waitForStep(5, 2000);
+        e.step(6);
+        m.remove(sa2);
+        m.remove(sa);
+        m.remove(sp2);
+        m.remove(sp);
+        e.step(7);
+    }
+    
+    public static interface ServiceInterface {
+        public String invoke(String input);
+    }
+    
+    public static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        private final String m_name;
+        public ServiceProvider(Ensure e, String name) {
+            m_ensure = e;
+            m_name = name;
+        }
+        public String invoke(String input) {
+            return input + m_name;
+        }
+    }
+    
+    public static class ServiceAspect implements ServiceInterface {
+        private volatile ServiceInterface m_originalService;
+        private volatile ServiceRegistration m_registration;
+        
+        public String invoke(String input) {
+            String result = m_originalService.invoke(input);
+            String property = (String) m_registration.getReference().getProperty("name");
+            return result + property;
+        }
+    }
+
+    public static class ServiceConsumer {
+        private final Ensure m_ensure;
+        private volatile ServiceInterface m_service;
+        private List<ServiceInterface> m_services = new ArrayList<ServiceInterface>();
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void add(ServiceReference ref, ServiceInterface si) {
+            System.out.println("add: " + ServiceUtil.toString(ref));
+            m_services.add(si);
+            m_ensure.step();
+        }
+        
+        public void remove(ServiceReference ref, ServiceInterface si) {
+            System.out.println("rem: " + ServiceUtil.toString(ref));
+            m_services.remove(si);
+            m_ensure.step();
+        }
+        
+        public String invoke() {
+            return m_service.invoke("");
+        }
+        
+        public List<String> invokeAll() {
+            List<String> results = new ArrayList<String>();
+            for (ServiceInterface si : m_services) {
+                results.add(si.invoke(""));
+            }
+            return results;
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectChainTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectChainTest.java
new file mode 100644
index 0000000..4d524fd
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectChainTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.dm.itest.api;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectChainTest extends TestBase {
+
+	public void testBuildAspectChain() {
+	    DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+        Component sa2 = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(new ServiceAspect(e, 3));
+        Component sa3 = m.createAspectService(ServiceInterface.class, null, 30, null).setImplementation(new ServiceAspect(e, 2));
+        Component sa1 = m.createAspectService(ServiceInterface.class, null, 10, null).setImplementation(new ServiceAspect(e, 4));
+        m.add(sc);
+
+        m.add(sp);
+        m.add(sa2);
+        m.add(sa3);
+        m.add(sa1);
+        e.step();
+        e.waitForStep(5,  5000);
+        
+        m.remove(sa3);
+        m.remove(sa2);
+        m.remove(sa1);
+        m.remove(sp);
+        
+        m.remove(sc);
+    }
+    
+    static interface ServiceInterface {
+        public void invoke(Runnable run);
+    }
+    
+    static class ServiceProvider implements ServiceInterface {
+        @SuppressWarnings("unused")
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke(Runnable run) {
+            run.run();
+        }
+    }
+    
+    static class ServiceAspect implements ServiceInterface {
+        private final Ensure m_ensure;
+        private volatile ServiceInterface m_parentService;
+        private final int m_step;
+        
+        public ServiceAspect(Ensure e, int step) {
+            m_ensure = e;
+            m_step = step;
+        }
+        public void start() {
+        }
+        
+        public void invoke(Runnable run) {
+            m_ensure.step(m_step);
+            m_parentService.invoke(run);
+        }
+        
+        public void stop() {
+        }
+    }
+
+    static class ServiceConsumer implements Runnable {
+        private volatile ServiceInterface m_service;
+        private final Ensure m_ensure;
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            Thread t = new Thread(this);
+            t.start();
+        }
+        
+        public void run() {
+            m_ensure.waitForStep(1, 2000);
+            m_service.invoke(Ensure.createRunnableStep(m_ensure, 5));
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectDynamicsTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectDynamicsTest.java
new file mode 100644
index 0000000..7177edb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectDynamicsTest.java
@@ -0,0 +1,194 @@
+/*
+ * 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.dm.itest.api;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectDynamicsTest extends TestBase {
+
+	public void testDynamicallyAddAndRemoveAspect() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        Ensure aspectStopEnsure = new Ensure();
+        // create a service provider and consumer
+        Component provider = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component provider2 = m.createComponent().setImplementation(new ServiceProvider2(e)).setInterface(ServiceInterface2.class.getName(), null);
+        Component consumer = m.createComponent().setImplementation(new ServiceConsumer(e))
+            .add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", null, null, "swap"));
+        Component aspect = m.createAspectService(ServiceInterface.class, null, 1, null).setImplementation(new ServiceAspect(e, aspectStopEnsure));
+        
+        m.add(consumer);
+        m.add(provider);
+        // the consumer should invoke the provider here, and when done, arrive at step 3
+        // finally wait for step 6 before continuing
+        e.waitForStep(3, 15000);
+        
+        m.add(aspect);
+        // after adding the aspect, we wait for its init to be invoked, arriving at
+        // step 4 after an instance bound dependency was added (on a service provided by
+        // provider 2)
+        e.waitForStep(4, 15000);
+        
+        m.add(provider2);
+        
+        // after adding provider 2, we should now see the client being swapped, so
+        // we wait for step 5 to happen
+        e.waitForStep(5, 15000);
+        
+        // now we continue with step 6, which will trigger the next part of the consumer's
+        // run method to be executed
+        e.step(6);
+        
+        // invoking step 7, 8 and 9 when invoking the aspect which in turn invokes the
+        // dependency and the original service, so we wait for that to finish here, which
+        // is after step 10 has been reached (the client will now wait for step 12)
+        e.waitForStep(10, 15000);
+        
+        m.remove(aspect);
+        aspectStopEnsure.waitForStep(1, 15000);
+        // removing the aspect should trigger step 11 (in the swap method of the consumer)
+        e.waitForStep(11, 15000);
+        
+        // step 12 triggers the client to continue
+        e.step(12);
+        
+        // wait for step 13, the final invocation of the provided service (without aspect)
+        e.waitForStep(13, 15000);
+        
+        // clean up
+        m.remove(provider2);
+        m.remove(provider);
+        m.remove(consumer);
+        e.waitForStep(16, 15000);
+        m.clear();
+    }
+    
+    static interface ServiceInterface {
+        public void invoke(Runnable run);
+    }
+    
+    static interface ServiceInterface2 {
+        public void invoke();
+    }
+    
+    static class ServiceProvider2 implements ServiceInterface2 {
+        private final Ensure m_ensure;
+
+        public ServiceProvider2(Ensure ensure) {
+            m_ensure = ensure;
+        }
+
+        public void invoke() {
+            m_ensure.step(9);
+        }
+        public void stop() {
+            m_ensure.step();
+        }
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke(Runnable run) {
+            run.run();
+        }
+        public void stop() {
+            m_ensure.step();
+        }
+    }
+    
+    static class ServiceAspect implements ServiceInterface {
+        private final Ensure m_ensure;
+        private volatile ServiceInterface m_originalService;
+        private volatile ServiceInterface2 m_injectedService;
+        private volatile Component m_service;
+        private volatile DependencyManager m_manager;
+        private final Ensure m_stopEnsure;
+        
+        public ServiceAspect(Ensure e, Ensure stopEnsure) {
+            m_ensure = e;
+            m_stopEnsure = stopEnsure;
+        }
+        public void init() {
+            m_service.add(m_manager.createServiceDependency()
+                .setService(ServiceInterface2.class)
+                .setRequired(true)
+            );
+            m_ensure.step(4);
+        }
+
+        public void invoke(Runnable run) {
+            m_ensure.step(7);
+            m_originalService.invoke(run);
+            m_injectedService.invoke();
+        }
+        
+        public void stop() {
+            m_stopEnsure.step(1);
+        }
+    }
+
+    static class ServiceConsumer implements Runnable {
+        private volatile ServiceInterface m_service;
+        private final Ensure m_ensure;
+        private final Ensure.Steps m_swapSteps = new Ensure.Steps(5, 11);
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        void add(ServiceInterface service) {
+            m_service = service;
+        }
+        
+        void swap(ServiceInterface oldService, ServiceInterface newService) {
+            System.out.println("swap: old=" + oldService + ", new=" + newService);
+            m_ensure.steps(m_swapSteps);
+            m_service = newService;
+        }
+        
+        public void init() {
+            Thread t = new Thread(this);
+            t.start();
+        }
+        
+        public void run() {
+            m_ensure.step(1);
+            m_service.invoke(Ensure.createRunnableStep(m_ensure, 2));
+            m_ensure.step(3);
+            m_ensure.waitForStep(6, 15000);
+            m_service.invoke(Ensure.createRunnableStep(m_ensure, 8));
+            m_ensure.step(10);
+            m_ensure.waitForStep(12, 15000);
+            m_service.invoke(Ensure.createRunnableStep(m_ensure, 13));
+        }
+        
+        public void stop() {
+            m_ensure.step();
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectRaceParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectRaceParallelTest.java
new file mode 100644
index 0000000..1559a75
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectRaceParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.dm.itest.api;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectRaceParallelTest extends AspectRaceTest {
+    public AspectRaceParallelTest() {
+        setParallel();
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectRaceTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectRaceTest.java
new file mode 100644
index 0000000..e8f6e0d
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectRaceTest.java
@@ -0,0 +1,324 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This class validates that some aspect aware services are correctly managed and ordered when components and aspects are 
+ * registered concurrently.
+ * 
+ * By default, this class uses a custom threadpool, but a subclass may override this class and call "setParallel()" method, in 
+ * this case we won't use any threadpool, since calling setParallel() method means we are using a parallel Dependency Manager.
+ * 
+ * @see AspectRaceParallelTest
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectRaceTest extends TestBase {
+    final static int SERVICES = 3;
+    final static int ASPECTS_PER_SERVICE = 10;
+    final static int ITERATIONS = 1000;
+    final AtomicInteger m_IDGenerator = new AtomicInteger();
+    ExecutorService m_threadpool;
+
+    public void testConcurrentAspects() {
+        try {
+            warn("starting aspect race test");
+            initThreadPool(); // only if setParallel() has not been called (only if a parallel DM is not used).
+
+            for (int loop = 1; loop <= ITERATIONS; loop++) {
+                // Perform concurrent injections of "S" service and S aspects into the Controller component;
+                debug("Iteration: " + loop);
+                
+                // Use a helper class to wait for components to be started/stopped.
+                int count = 1 /* for controller */ + SERVICES + (SERVICES * ASPECTS_PER_SERVICE);
+                ComponentTracker tracker = new ComponentTracker(count, count);
+                
+                // Create the components (controller / services / aspects)
+                Controller controller = new Controller();
+                Factory f = new Factory();
+                f.createComponents(controller, tracker);
+                
+                // Activate the components asynchronously
+                f.registerComponents();
+                
+                // Wait for the components to be started (using the tracker)
+                if (!tracker.awaitStarted(5000)) {
+                    throw new IllegalStateException("Could not start components timely.");
+                }
+                
+                // Check aspect chains consistency.
+                controller.checkConsistency();
+                
+                // unregister all services and aspects.
+                f.unregisterComponents();
+                
+                // use component tracker to wait for all components to be stopped.
+                if (!tracker.awaitStopped(5000)) {
+                    throw new IllegalStateException("Could not stop components timely.");
+                }
+
+                if ((loop) % 50 == 0) {
+                    warn("Performed " + loop + " tests.");
+                }
+
+                if (super.errorsLogged()) {
+                    throw new IllegalStateException("Race test interrupted (some error occured, see previous logs)");
+                }
+            }
+        }
+
+        catch (Throwable t) {
+            error("Test failed", t);
+            Assert.fail("Test failed: " + t.getMessage());
+        } finally {
+            m_dm.clear();
+            shutdownThreadPool();
+        }
+    }
+
+    private void initThreadPool() {
+        // Create a threadpool only if setParallel() method has not been called.
+        if (! m_parallel) {
+            int cores = Math.max(16, Runtime.getRuntime().availableProcessors());
+            m_threadpool = Executors.newFixedThreadPool(cores);
+        }
+    }
+    
+    void shutdownThreadPool() {
+        if (! m_parallel && m_threadpool != null) {
+            m_threadpool.shutdown();
+            try {
+                m_threadpool.awaitTermination(60, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
+    public interface S {
+        void invoke(Ensure e);
+
+        int getRank();
+    }
+
+    public static class SImpl implements S {
+
+        SImpl() {
+        }
+
+        public void invoke(Ensure e) {
+            e.step(1);
+        }
+
+        public String toString() {
+            return "SImpl";
+        }
+
+        @Override
+        public int getRank() {
+            return Integer.MIN_VALUE;
+        }
+    }
+
+    public class SAspect implements S {
+        volatile S m_next;
+        final int m_rank;
+        volatile Component m_component;
+
+        SAspect(int rank) {
+            m_rank = rank;
+        }
+
+        public synchronized void added(S s) {
+            debug("aspect.added: this rank=%d, next rank=%d", getRank(), s.getRank());
+            m_next = s;
+        }
+
+        public synchronized void swap(S oldS, S newS) {
+            debug("aspect.swap: this rank=%d, old rank=%d, next rank=%d", getRank(), oldS.getRank(), newS.getRank());
+            m_next = newS;
+        }
+
+        public synchronized void removed(S s) {
+            debug("aspect.remove: this rank=%d, removed rank=%d", getRank(), s.getRank());
+            m_next = null;
+        }
+
+        public synchronized void invoke(Ensure e) {
+            debug("aspect.invoke: this rank=%d, next rank=%d", this.getRank(), m_next.getRank());
+            Assert.assertTrue(m_rank > m_next.getRank());
+            m_next.invoke(e);
+        }
+
+        public String toString() {
+            return "[Aspect/rank=" + m_rank + "], next="
+                + ((m_next != null) ? m_next : "null");
+        }
+
+        @Override
+        public int getRank() {
+            return m_rank;
+        }
+    }
+
+    class Factory {
+        int m_serviceId;
+        Component m_controller;
+        final ConcurrentLinkedQueue<Component> m_services = new ConcurrentLinkedQueue<Component>();
+        final ConcurrentLinkedQueue<Component> m_aspects = new ConcurrentLinkedQueue<Component>();   
+        
+        private void createComponents(Controller controller, ComponentTracker tracker) {
+            // create the controller
+            int controllerID = m_IDGenerator.incrementAndGet();
+            m_controller = m_dm.createComponent()
+                .setImplementation(controller)
+                .setComposition("getComposition")
+                .add(tracker);
+            for (int i = 0; i < SERVICES; i ++) {
+                m_controller.add(m_dm.createServiceDependency()
+                    .setService(S.class, "(controller.id=" + controllerID + ")")
+                    .setCallbacks("bind", null, "unbind", "swap")
+                    .setRequired(true));
+            }
+            
+            // create the services
+            for (int i = 1; i <= SERVICES; i++) {
+                int aspectId = m_IDGenerator.incrementAndGet();
+                Component s = m_dm.createComponent();
+                Hashtable<String, String> props = new Hashtable<String, String>();
+                props.put("controller.id", String.valueOf(controllerID));
+                props.put("aspect.id", String.valueOf(aspectId));
+                s.setInterface(S.class.getName(), props)
+                 .setImplementation(new SImpl());
+                s.add(tracker);
+                m_services.add(s);
+                
+                // create the aspects for that service
+                for (int j = 1; j <= ASPECTS_PER_SERVICE; j++) {
+                    final int rank = j;
+                    SAspect sa = new SAspect(rank);
+                    Component a = 
+                        m_dm.createAspectService(S.class, "(aspect.id=" + aspectId + ")", rank, "added", null, "removed", "swap")
+                            .setImplementation(sa);
+                    a.add(tracker);
+                    m_aspects.add(a);
+                }
+            }
+        }
+
+        public void registerComponents() {
+            // If setParallel() has been called (we are using a parallel dependency manager), then no needs to use a custom thread pool.
+            if (m_parallel) { // using a parallel DM.
+                for (final Component s : m_services) {
+                    m_dm.add(s);
+                }
+                m_dm.add(m_controller);
+                for (final Component a : m_aspects) {
+                    m_dm.add(a);
+                }
+            } else {
+                for (final Component s : m_services) {
+                    m_threadpool.execute(new Runnable() {
+                        public void run() {
+                            m_dm.add(s);
+                        }
+                    });
+                }
+                m_threadpool.execute(new Runnable() {
+                    public void run() {
+                        m_dm.add(m_controller);
+                    }
+                });
+                for (final Component a : m_aspects) {
+                    m_threadpool.execute(new Runnable() {
+                        public void run() {
+                            m_dm.add(a);
+                        }
+                    });
+                }
+            }
+        }
+
+        public void unregisterComponents() throws InterruptedException, InvalidSyntaxException {        
+            m_dm.remove(m_controller);
+            for (final Component s : m_services) {
+                m_dm.remove(s);
+            }
+            for (final Component a : m_aspects) {
+                m_dm.remove(a);
+            }
+        }
+    }
+
+    public class Controller {
+        final Composition m_compo = new Composition();
+        final HashSet<S> m_services = new HashSet<S>();
+
+        Object[] getComposition() {
+            return new Object[] { this, m_compo };
+        }
+
+        synchronized void bind(ServiceReference sr, Object service) {
+            debug("controller.bind: %s", service);
+            S s = (S) service;
+            m_services.add(s);
+            debug("bind: service count after bind: %d", m_services.size());
+        }
+
+        synchronized void swap(S previous, S current) {
+            debug("controller.swap: previous=%s, current=%s", previous, current);
+            if (!m_services.remove(previous)) {
+                debug("swap: unknow previous service: " + previous);
+            }
+            m_services.add(current);
+            debug("controller.swap: service count after swap: %d", m_services.size());
+        }
+
+        synchronized void unbind(S a) {
+            debug("unbind " + a);
+            m_services.remove(a);
+        }
+        
+        synchronized void checkConsistency() {
+            debug("service count: %d", m_services.size());
+            for (S s : m_services) {
+                info("checking service: %s", s);
+                Ensure ensure = new Ensure(false);
+                s.invoke(ensure);
+            }
+        }
+    }
+
+    public static class Composition {
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectServiceDependencyTest.java
new file mode 100644
index 0000000..365ee31
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectServiceDependencyTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.dm.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectServiceDependencyTest extends TestBase {
+    public void testServiceRegistrationAndConsumption() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency()
+        		.setService(ServiceInterface.class)
+        		.setCallbacks("add", "remove")
+        		.setRequired(true));
+        Component asp = m.createAspectService(ServiceInterface.class, null, 100)
+        		.setImplementation(ServiceProviderAspect.class);
+        m.add(sp);
+        m.add(sc);
+        m.add(asp);
+        m.remove(asp); 
+        m.remove(sc);
+        m.remove(sp);
+        
+        // ensure we executed all steps inside the component instance
+        e.step(8);
+    }
+    
+    static interface ServiceInterface {
+        public void invoke(String caller);
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke(String caller) {
+        	if (caller.equals("consumer.init")) {
+        		m_ensure.step(3);
+        	} else if (caller.equals("aspect.consumer.add")) {
+        		m_ensure.step(5);
+        	}
+        }
+    }
+    
+    static class ServiceProviderAspect implements ServiceInterface {
+    	private volatile ServiceInterface m_service;
+    	
+    	public ServiceProviderAspect() {
+		}
+
+		@Override
+		public void invoke(String caller) {
+			m_service.invoke("aspect." + caller);
+		}
+    }
+
+    static class ServiceConsumer {
+        private volatile ServiceInterface m_service;
+        private final Ensure m_ensure;
+        private int addCount = 0;
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            m_ensure.step(2);
+            m_service.invoke("consumer.init");
+        }
+        
+        public void destroy() {
+            m_ensure.step(7);
+        }
+        
+        public void add(ServiceInterface service) {
+        	m_service = service;
+        	switch (addCount) {
+        		case 0: m_ensure.step(1);
+        				break;
+        		case 1: m_ensure.step(4);
+        				// aspect had been added
+        				m_service.invoke("consumer.add");
+        				break;
+        		case 2: m_ensure.step(6);
+        				break;
+        		default:
+        	}
+        	addCount ++;
+        }
+        public void remove(ServiceInterface service) {
+        	m_service = null;
+        }    
+    }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectServiceDependencyWithSwapCallbackTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectServiceDependencyWithSwapCallbackTest.java
new file mode 100644
index 0000000..03ce7c1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectServiceDependencyWithSwapCallbackTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.dm.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectServiceDependencyWithSwapCallbackTest extends TestBase {
+    public void testServiceRegistrationAndConsumption() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency()
+        		.setService(ServiceInterface.class)
+        		.setCallbacks("add", null, "remove", "swap")
+        		.setRequired(true));
+        Component asp = m.createAspectService(ServiceInterface.class, null, 100)
+        		.setImplementation(ServiceProviderAspect.class);
+        m.add(sp);
+        m.add(sc);
+        m.add(asp);
+        m.remove(asp); 
+        m.remove(sc);
+        m.remove(sp);
+        
+        // ensure we executed all steps inside the component instance
+        e.step(7);
+    }
+    
+    static interface ServiceInterface {
+        public void invoke(String caller);
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke(String caller) {
+        	if (caller.equals("consumer.init")) {
+        		m_ensure.step(3);
+        	} else if (caller.equals("aspect.consumer.add")) {
+        		m_ensure.step(5);
+        	}
+        }
+    }
+    
+    static class ServiceProviderAspect implements ServiceInterface {
+    	private volatile ServiceInterface m_service;
+    	
+    	public ServiceProviderAspect() {
+		}
+
+		@Override
+		public void invoke(String caller) {
+			m_service.invoke("aspect." + caller);
+		}
+    }
+
+    static class ServiceConsumer {
+        private volatile ServiceInterface m_service;
+        private final Ensure m_ensure;
+        private int swapCount = 0;
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            m_ensure.step(2);
+            m_service.invoke("consumer.init");
+        }
+        
+        public void destroy() {
+            m_ensure.step(6);
+        }
+        
+        public void add(ServiceInterface service) {
+        	m_service = service;
+        	m_ensure.step(1);
+        }
+        
+        public void remove(ServiceInterface service) {
+        	m_service = null;
+        }
+        
+        public void swap(ServiceInterface previous, ServiceInterface current) {
+        	switch (swapCount) {
+        		case 0: m_ensure.step(4);
+        				break;
+        		case 1: m_ensure.step(5);
+        				break;
+        		default:
+        	}
+        	m_service = current;
+        	swapCount ++;
+        }
+    }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWhiteboardTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWhiteboardTest.java
new file mode 100644
index 0000000..4e4181d
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWhiteboardTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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.dm.itest.api;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.ServiceUtil;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"rawtypes", "unchecked", "unused"})
+public class AspectWhiteboardTest extends TestBase {
+
+	public void testWhiteboardConsumer() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create service providers and consumer
+        Component sp1 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sp2 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        ServiceConsumer sci = new ServiceConsumer(e);
+        Component sc = m.createComponent().setImplementation(sci).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(false).setCallbacks("add", "remove"));
+        Component sa2 = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(new ServiceAspect(e, 3));
+        Component sa1 = m.createAspectService(ServiceInterface.class, null, 10, null).setImplementation(new ServiceAspect(e, 4));
+        
+        // start with a service consumer
+        System.out.println("Adding consumer");
+        m.add(sc);
+        
+        // then add two providers, so the consumer will see two services
+        System.out.println("Adding 2 providers");
+        m.add(sp1);
+        m.add(sp2);
+        
+        // make sure consumer sees both services
+        Assert.assertEquals(2, sci.services());
+        
+        // add an aspect with ranking 20
+        System.out.println("Adding aspect with rank 20");
+        m.add(sa2);
+        
+        // make sure the consumer sees the two new aspects and no longer sees the two original services
+        Assert.assertEquals(2, sci.services());
+        Assert.assertEquals(20, sci.highestRanking());
+        Assert.assertEquals(20, sci.lowestRanking());
+        
+        // add an aspect with ranking 10
+        System.out.println("Adding aspect with rank 10");
+        m.add(sa1);
+        
+        // make sure the consumer still sees the two aspects with ranking 20
+        Assert.assertEquals(2, sci.services());
+        Assert.assertEquals(20, sci.highestRanking());
+        Assert.assertEquals(20, sci.lowestRanking());
+        
+        // remove the aspect with ranking 20
+        System.out.println("Removing aspect with rank 20");
+        m.remove(sa2);
+        
+        // make sure the consumer now sees the aspects with ranking 10
+        Assert.assertEquals(2, sci.services());
+        Assert.assertEquals(10, sci.highestRanking());
+        Assert.assertEquals(10, sci.lowestRanking());
+        
+        // remove one of the original services
+        System.out.println("Removing 1 service");
+        m.remove(sp1);
+        
+        // make sure the aspect of that service goes away
+        Assert.assertEquals(1, sci.services());
+        Assert.assertEquals(10, sci.highestRanking());
+        Assert.assertEquals(10, sci.lowestRanking());
+        
+        // remove the aspect with ranking 10
+        System.out.println("Removing aspect with rank 10");
+        m.remove(sa1);
+        
+        // make sure only the original service remains
+        Assert.assertEquals(1, sci.services());
+        Assert.assertEquals(0, sci.highestRanking());
+        Assert.assertEquals(0, sci.lowestRanking());
+
+        System.out.println("Done with test");
+
+        // end of test
+        m.remove(sa2);
+        m.remove(sp2);
+        m.remove(sc);
+    }
+    
+    static interface ServiceInterface {
+        public void invoke(Runnable run);
+    }
+    
+    static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke(Runnable run) {
+            run.run();
+        }
+    }
+    
+    static class ServiceAspect implements ServiceInterface {
+        private final Ensure m_ensure;
+        private volatile ServiceInterface m_parentService;
+        private final int m_step;
+        
+        public ServiceAspect(Ensure e, int step) {
+            m_ensure = e;
+            m_step = step;
+        }
+        public void start() {
+        }
+        
+        public void invoke(Runnable run) {
+            m_ensure.step(m_step);
+            m_parentService.invoke(run);
+        }
+        
+        public void stop() {
+        }
+    }
+
+    static class ServiceConsumer implements Runnable {
+        private List m_services = new ArrayList();
+        private final Ensure m_ensure;
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            Thread t = new Thread(this);
+            t.start();
+        }
+        
+        public void run() {
+        }
+        
+        public int services() {
+            return m_services.size();
+        }
+        
+        public int highestRanking() {
+            int ranking = Integer.MIN_VALUE;
+            for (int i = 0; i < m_services.size(); i++) {
+                ServiceReference ref = (ServiceReference) m_services.get(i);
+                Integer r = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
+                int rank = r == null ? 0 : r.intValue();
+                ranking = Math.max(ranking, rank);
+            }
+            return ranking;
+        }
+        public int lowestRanking() {
+            int ranking = Integer.MAX_VALUE;
+            for (int i = 0; i < m_services.size(); i++) {
+                ServiceReference ref = (ServiceReference) m_services.get(i);
+                Integer r = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
+                int rank = r == null ? 0 : r.intValue();
+                ranking = Math.min(ranking, rank);
+            }
+            return ranking;
+        }
+        
+        public void add(ServiceReference ref, ServiceInterface svc) {
+            System.out.println("Added: " + ServiceUtil.toString(ref));
+            m_services.add(ref);
+        }
+        public void remove(ServiceReference ref, ServiceInterface svc) {
+            System.out.println("Removed: " + ServiceUtil.toString(ref));
+            m_services.remove(ref);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithCallbacksServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithCallbacksServiceDependencyTest.java
new file mode 100644
index 0000000..c5116f2
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithCallbacksServiceDependencyTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.dm.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AspectWithCallbacksServiceDependencyTest extends TestBase {
+    public void testServiceRegistrationAndConsumption() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency()
+        		.setService(ServiceInterface.class)
+        		.setCallbacks("add", "remove")
+        		.setRequired(true));
+        Component asp = m.createAspectService(ServiceInterface.class, null, 100, "add", null, "remove", "swap")
+        		.setImplementation(ServiceProviderAspect.class);
+        m.add(sp);
+        m.add(sc);
+        m.add(asp);
+        m.remove(asp); 
+        m.remove(sc);
+        m.remove(sp);
+        
+        // ensure we executed all steps inside the component instance
+        e.step(8);
+    }
+    
+    static interface ServiceInterface {
+        public void invoke(String caller);
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke(String caller) {
+        	if (caller.equals("consumer.init")) {
+        		m_ensure.step(3);
+        	} else if (caller.equals("aspect.consumer.add")) {
+        		m_ensure.step(5);
+        	}
+        }
+    }
+    
+    static class ServiceProviderAspect implements ServiceInterface {
+    	private volatile ServiceInterface m_service;
+    	
+    	public ServiceProviderAspect() {
+		}
+
+		@Override
+		public void invoke(String caller) {
+			m_service.invoke("aspect." + caller);
+		}
+		
+		public void add(ServiceInterface service) {
+			m_service = service;
+		}
+		
+		public void remove(ServiceInterface service) {
+			m_service = null;
+		}
+		
+		public void swap(ServiceInterface previous, ServiceInterface current) {
+			m_service = current;
+		}
+    }
+
+    static class ServiceConsumer {
+        private volatile ServiceInterface m_service;
+        private final Ensure m_ensure;
+        private int addCount = 0;
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            m_ensure.step(2);
+            m_service.invoke("consumer.init");
+        }
+        
+        public void destroy() {
+            m_ensure.step(7);
+        }
+        
+        public void add(ServiceInterface service) {
+        	m_service = service;
+        	switch (addCount) {
+        		case 0: m_ensure.step(1);
+        				break;
+        		case 1: m_ensure.step(4);
+        				// aspect had been added
+        				m_service.invoke("consumer.add");
+        				break;
+        		case 2: m_ensure.step(6);
+        				break;
+        		default:
+        	}
+        	addCount ++;
+        }
+        public void remove(ServiceInterface service) {
+        	m_service = null;
+        }    
+    }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithPropagationTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithPropagationTest.java
new file mode 100644
index 0000000..f90c264
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AspectWithPropagationTest.java
@@ -0,0 +1,763 @@
+/*
+ * 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.dm.itest.api;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.ServiceUtil;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Test for aspects with service properties propagations.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"rawtypes", "unchecked", "unused"})
+public class AspectWithPropagationTest extends TestBase {
+    private final static int ASPECTS = 3;
+    private final Set<Integer> _randoms = new HashSet<Integer>();
+    private final Random _rnd = new Random();
+    private static Ensure m_invokeStep;
+    private static Ensure m_changeStep;
+    
+    /**
+     * This test does the following:
+     * 
+     * - Create S service with property "p=s"
+     * - Create SA (aspect of S) with property "p=aspect"
+     * - Create Client, depending on S (actually, on SA).
+     * - Client should see SA with properties p=aspect
+     * - Change S service property with "p=smodified": the Client should be changed with SA(p=aspect)
+     * - Change aspect service property with "p=aspectmodified": The client should be changed with SA(p=aspectmodified)
+     */
+    public void testAspectsWithPropagationNotOverriding() {
+        System.out.println("----------- Running testAspectsWithPropagationNotOverriding ...");
+        DependencyManager m = getDM();
+        m_invokeStep = new Ensure(); 
+        
+        // Create our original "S" service.
+        S s = new S() {
+			public void invoke() {
+			}
+        };
+		Dictionary props = new Hashtable();
+		props.put("p", "s");
+		Component sComp = m.createComponent()
+                .setImplementation(s)
+                .setInterface(S.class.getName(), props);
+
+        // Create SA (aspect of S)
+        S sa = new S() {
+        	volatile S m_s;
+			public void invoke() {
+			}
+        };
+        Component saComp = m.createAspectService(S.class, null, 1).setImplementation(sa);        		
+        props = new Hashtable();
+        props.put("p", "aspect");
+        saComp.setServiceProperties(props);
+        
+        // Create client depending on S
+        Object client = new Object() {
+        	int m_changeCount;
+        	void add(Map props, S s) {
+        		Assert.assertEquals("aspect", props.get("p"));
+        		m_invokeStep.step(1);
+        	}
+        	
+            void change(Map props, S s) {
+        		switch (++m_changeCount) {
+        		case 1:
+        			Assert.assertEquals("aspect", props.get("p"));
+            		m_invokeStep.step(2);
+            		break;
+        		case 2:
+        			Assert.assertEquals("aspectmodified", props.get("p"));
+            		m_invokeStep.step(3);
+        		}
+        	}
+        };
+        Component clientComp = m.createComponent()
+                .add(m.createServiceDependency()
+                     .setService(S.class)
+                     .setRequired(true)
+                     .setCallbacks("add", "change", null))
+                .setImplementation(client);
+        
+        // Add components in dependency manager
+        m.add(sComp);
+        m.add(saComp);
+        m.add(clientComp);
+        
+        // client should have been added with SA aspect
+        m_invokeStep.waitForStep(1, 5000);
+        
+        // now change s "p=s" to "p=smodified": client should not see it
+        props = new Hashtable();
+        props.put("p", "smodified");
+        sComp.setServiceProperties(props);
+        m_invokeStep.waitForStep(2, 5000);
+        
+        // now change sa aspect "p=aspect" to "p=aspectmodified": client should see it
+        props = new Hashtable();
+        props.put("p", "aspectmodified");
+        saComp.setServiceProperties(props);
+        m_invokeStep.waitForStep(3, 5000);    
+        
+        // remove components
+        m.remove(clientComp);
+        m.remove(saComp);
+        m.remove(sComp);
+    }
+        
+    /**
+     * This test does the following:
+     * 
+     * - Create S service
+     * - Create some S Aspects
+     * - Create a Client, depending on S (actually, on the top-level S aspect)
+     * - Client has a "change" callback in order to track S service properties modifications.
+     * - First, invoke Client.invoke(): all S aspects, and finally original S service must be invoked orderly.
+     * - Modify S original service properties, and check if all aspects, and the client has been orderly called in their "change" callback.
+     * - Modify the First lowest ranked aspect (rank=1), and check if all aspects, and client have been orderly called in their "change" callback.
+     */
+    public void testAspectsWithPropagation() {
+        System.out.println("----------- Running testAspectsWithPropagation ...");
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        m_invokeStep = new Ensure(); 
+        
+        // Create our original "S" service.
+        Dictionary props = new Hashtable();
+        props.put("foo", "bar");
+        Component s = m.createComponent()
+                .setImplementation(new SImpl())
+                .setInterface(S.class.getName(), props);
+        
+        // Create an aspect aware client, depending on "S" service.
+        Client clientImpl;
+        Component client = m.createComponent()
+                .setImplementation((clientImpl = new Client()))
+                .add(m.createServiceDependency()
+                     .setService(S.class)
+                     .setRequired(true)
+                     .setCallbacks("add", "change", "remove", "swap"));
+
+        // Create some "S" aspects
+        Component[] aspects = new Component[ASPECTS];
+        for (int rank = 1; rank <= ASPECTS; rank ++) {
+            aspects[rank-1] = m.createAspectService(S.class, null, rank, "add", "change", "remove", "swap")
+                    .setImplementation(new A("A" + rank, rank));
+            props = new Hashtable();
+            props.put("a" + rank, "v" + rank);
+            aspects[rank-1].setServiceProperties(props);
+        }                                    
+              
+        // Register client
+        m.add(client);
+        
+        // Randomly register aspects and original service
+        boolean originalServiceAdded = false;
+        for (int i = 0; i < ASPECTS; i ++) {
+            int index = getRandomAspect();
+            m.add(aspects[index]);
+            if (! originalServiceAdded && _rnd.nextBoolean()) {
+                m.add(s);
+                originalServiceAdded = true;
+            }
+        }
+        if (! originalServiceAdded) {
+            m.add(s);
+        }
+              
+        // All set, check if client has inherited from top level aspect properties + original service properties
+        Map check = new HashMap();
+        check.put("foo", "bar");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS);
+        checkServiceProperties(check, clientImpl.getServiceProperties());
+
+        // Now invoke client, which orderly calls all aspects in the chain, and finally the original service "S".
+        System.out.println("-------------------------- Invoking client.");
+        clientImpl.invoke();
+        m_invokeStep.waitForStep(ASPECTS+1, 5000);
+        
+        // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, and on client. 
+        System.out.println("-------------------------- Modifying original service properties.");
+        m_changeStep = new Ensure();
+        props = new Hashtable();
+        props.put("foo", "barModified");
+        s.setServiceProperties(props);
+        
+        // Check if aspects and client have been orderly called in their "changed" callback
+        m_changeStep.waitForStep(ASPECTS+1, 5000);
+        
+        // Check if modified "foo" original service property has been propagated
+        check = new HashMap();
+        check.put("foo", "barModified");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS); // we only see top-level aspect service properties
+        checkServiceProperties(check, clientImpl.getServiceProperties());
+        
+        // Now, change the top-level ranked aspect: it must propagate to all upper aspects, as well as to the client
+        System.out.println("-------------------------- Modifying top-level aspect service properties.");
+
+        m_changeStep = new Ensure();
+        for (int i = 1; i <= ASPECTS; i ++) {
+            m_changeStep.step(i); // only client has to be changed.
+        }
+        props = new Hashtable();
+        props.put("a" + ASPECTS, "v" + ASPECTS + "-Modified");
+        aspects[ASPECTS-1].setServiceProperties(props); // That triggers change callbacks for upper aspects (with rank >= 2)
+        m_changeStep.waitForStep(ASPECTS+1, 5000); // check if client have been changed.
+        
+        // Check if top level aspect service properties have been propagated up to the client.
+        check = new HashMap();
+        check.put("foo", "barModified");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS + "-Modified");
+        checkServiceProperties(check, clientImpl.getServiceProperties());
+
+        // Clear all components.
+        m_changeStep = null;
+        m.clear();
+    }    
+    
+    /**
+     * This test does the following:
+     * 
+     * - Create S service
+     * - Create some S Aspects without any callbacks (add/change/remove/swap)
+     * - Create a Client, depending on S (actually, on the top-level S aspect)
+     * - Client has a "change" callack in order to track S service properties modifications.
+     * - First, invoke Client.invoke(): all S aspects, and finally original S service must be invoked orderly.
+     * - Modify S original service properties, and check if the client has been called in its "change" callback.
+     */
+    public void testAspectsWithPropagationAndNoCallbacks() {
+        System.out.println("----------- Running testAspectsWithPropagation ...");
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        m_invokeStep = new Ensure(); 
+        
+        // Create our original "S" service.
+        Dictionary props = new Hashtable();
+        props.put("foo", "bar");
+        Component s = m.createComponent()
+                .setImplementation(new SImpl())
+                .setInterface(S.class.getName(), props);
+        
+        // Create an aspect aware client, depending on "S" service.
+        Client clientImpl;
+        Component client = m.createComponent()
+                .setImplementation((clientImpl = new Client()))
+                .add(m.createServiceDependency()
+                     .setService(S.class)
+                     .setRequired(true)
+                     .setCallbacks("add", "change", "remove"));
+
+        // Create some "S" aspects
+        Component[] aspects = new Component[ASPECTS];
+        for (int rank = 1; rank <= ASPECTS; rank ++) {
+            aspects[rank-1] = m.createAspectService(S.class, null, rank)
+                    .setImplementation(new A("A" + rank, rank));
+            props = new Hashtable();
+            props.put("a" + rank, "v" + rank);
+            aspects[rank-1].setServiceProperties(props);
+        }                                    
+              
+        // Register client
+        m.add(client);
+        
+        // Randomly register aspects and original service
+        boolean originalServiceAdded = false;
+        for (int i = 0; i < ASPECTS; i ++) {
+            int index = getRandomAspect();
+            m.add(aspects[index]);
+            if (! originalServiceAdded && _rnd.nextBoolean()) {
+                m.add(s);
+                originalServiceAdded = true;
+            }
+        }
+        if (! originalServiceAdded) {
+            m.add(s);
+        }
+              
+        // All set, check if client has inherited from top level aspect properties + original service properties
+        Map check = new HashMap();
+        check.put("foo", "bar");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS);
+        checkServiceProperties(check, clientImpl.getServiceProperties());
+
+        // Now invoke client, which orderly calls all aspects in the chain, and finally the original service "S".
+        System.out.println("-------------------------- Invoking client.");
+        clientImpl.invoke();
+        m_invokeStep.waitForStep(ASPECTS+1, 5000);
+        
+        // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, and on client. 
+        System.out.println("-------------------------- Modifying original service properties.");
+        m_changeStep = new Ensure();
+        for (int i = 1; i <= ASPECTS; i ++) {
+            m_changeStep.step(i); // skip aspects, which have no "change" callbacks.
+        }
+        props = new Hashtable();
+        props.put("foo", "barModified");
+        s.setServiceProperties(props);
+        
+        // Check if aspects and client have been orderly called in their "changed" callback
+        m_changeStep.waitForStep(ASPECTS+1, 5000);
+        
+        // Check if modified "foo" original service property has been propagated
+        check = new HashMap();
+        check.put("foo", "barModified");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS); // we only see top-level aspect service properties
+        checkServiceProperties(check, clientImpl.getServiceProperties());
+        
+        // Clear all components.
+        m_changeStep = null;
+        m.clear();
+    }    
+    
+    /**
+     * This test does the following:
+     * 
+     * - Create S service
+     * - Create some S Aspects
+     * - Create S2 Adapter, which adapts S to S2
+     * - Create Client2, which depends on S2. Client2 listens to S2 property change events.
+     * - Now, invoke Client2.invoke(): all S aspects, and finally original S service must be invoked orderly.
+     * - Modify S original service properties, and check if all aspects, S2 Adapter, and Client2 have been orderly called in their "change" callback.
+     */
+    public void testAdapterWithAspectsAndPropagation() {
+        System.out.println("----------- Running testAdapterWithAspectsAndPropagation ...");
+
+        DependencyManager m = getDM();
+        m_invokeStep = new Ensure(); 
+        
+        // Create our original "S" service.
+        Dictionary props = new Hashtable();
+        props.put("foo", "bar");
+        Component s = m.createComponent()
+                .setImplementation(new SImpl())
+                .setInterface(S.class.getName(), props);
+        
+        // Create some "S" aspects
+        Component[] aspects = new Component[ASPECTS];
+        for (int rank = 1; rank <= ASPECTS; rank ++) {
+            aspects[rank-1] = m.createAspectService(S.class, null, rank, "add", "change", "remove", "swap")
+                    .setImplementation(new A("A" + rank, rank));
+            props = new Hashtable();
+            props.put("a" + rank, "v" + rank);
+            aspects[rank-1].setServiceProperties(props);
+        } 
+        
+        // Create S2 adapter (which adapts S1 to S2 interface)
+        Component adapter = m.createAdapterService(S.class, null, "add", "change", "remove", "swap")
+                .setInterface(S2.class.getName(), null)
+                .setImplementation(new S2Impl());
+        
+        // Create Client2, which depends on "S2" service.
+        Client2 client2Impl;
+        Component client2 = m.createComponent()
+                .setImplementation((client2Impl = new Client2()))
+                .add(m.createServiceDependency()
+                     .setService(S2.class)
+                     .setRequired(true)
+                     .setCallbacks("add", "change", null));
+              
+        // Register client2
+        m.add(client2);
+        
+        // Register S2 adapter
+        m.add(adapter);
+        
+        // Randomly register aspects, original service
+        boolean originalServiceAdded = false;
+        for (int i = 0; i < ASPECTS; i ++) {
+            int index = getRandomAspect();
+            m.add(aspects[index]);
+            if (! originalServiceAdded && _rnd.nextBoolean()) {
+                m.add(s);
+                originalServiceAdded = true;
+            }
+        }
+        if (! originalServiceAdded) {
+            m.add(s);
+        }
+             
+        // Now invoke client2, which orderly calls all S1 aspects, then S1Impl, and finally S2 service
+        System.out.println("-------------------------- Invoking client2.");
+        client2Impl.invoke2();
+        m_invokeStep.waitForStep(ASPECTS+2, 5000);
+        
+        // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, S2Impl, and Client2.
+        System.out.println("-------------------------- Modifying original service properties.");
+        m_changeStep = new Ensure();
+        props = new Hashtable();
+        props.put("foo", "barModified");
+        s.setServiceProperties(props);
+        
+        // Check if aspects and Client2 have been orderly called in their "changed" callback
+        m_changeStep.waitForStep(ASPECTS+2, 5000);
+        
+        // Check if modified "foo" original service property has been propagated to Client2
+        Map check = new HashMap();
+        check.put("foo", "barModified");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS);
+        checkServiceProperties(check, client2Impl.getServiceProperties());
+        
+        // Clear all components.
+        m_changeStep = null;
+        m.clear();
+    }    
+    
+    /**
+     * This test does the following:
+     * 
+     * - Create S service
+     * - Create some S Aspects without any callbacks (add/change/remove)
+     * - Create S2 Adapter, which adapts S to S2 (but does not have any add/change/remove callbacks)
+     * - Create Client2, which depends on S2. Client2 listens to S2 property change events.
+     * - Now, invoke Client2.invoke(): all S aspects, and finally original S service must be invoked orderly.
+     * - Modify S original service properties, and check if all aspects, S2 Adapter, and Client2 have been orderly called in their "change" callback.
+     */
+    public void testAdapterWithAspectsAndPropagationNoCallbacks() {
+        System.out.println("----------- Running testAdapterWithAspectsAndPropagationNoCallbacks ...");
+
+        DependencyManager m = getDM();
+        m_invokeStep = new Ensure(); 
+        
+        // Create our original "S" service.
+        Dictionary props = new Hashtable();
+        props.put("foo", "bar");
+        Component s = m.createComponent()
+                .setImplementation(new SImpl())
+                .setInterface(S.class.getName(), props);
+        
+        // Create some "S" aspects
+        Component[] aspects = new Component[ASPECTS];
+        for (int rank = 1; rank <= ASPECTS; rank ++) {
+            aspects[rank-1] = m.createAspectService(S.class, null, rank)
+                    .setImplementation(new A("A" + rank, rank));
+            props = new Hashtable();
+            props.put("a" + rank, "v" + rank);
+            aspects[rank-1].setServiceProperties(props);
+        } 
+        
+        // Create S2 adapter (which adapts S1 to S2 interface)
+        Component adapter = m.createAdapterService(S.class, null)
+                .setInterface(S2.class.getName(), null)
+                .setImplementation(new S2Impl());
+        
+        // Create Client2, which depends on "S2" service.
+        Client2 client2Impl;
+        Component client2 = m.createComponent()
+                .setImplementation((client2Impl = new Client2()))
+                .add(m.createServiceDependency()
+                     .setService(S2.class)
+                     .setRequired(true)
+                     .setCallbacks("add", "change", null));
+              
+        // Register client2
+        m.add(client2);
+        
+        // Register S2 adapter
+        m.add(adapter);
+        
+        // Randomly register aspects, original service
+        boolean originalServiceAdded = false;
+        for (int i = 0; i < ASPECTS; i ++) {
+            int index = getRandomAspect();
+            m.add(aspects[index]);
+            if (! originalServiceAdded && _rnd.nextBoolean()) {
+                m.add(s);
+                originalServiceAdded = true;
+            }
+        }
+        if (! originalServiceAdded) {
+            m.add(s);
+        }
+             
+        // Now invoke client2, which orderly calls all S1 aspects, then S1Impl, and finally S2 service
+        System.out.println("-------------------------- Invoking client2.");
+        client2Impl.invoke2();
+        m_invokeStep.waitForStep(ASPECTS+2, 5000);
+        
+        // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, S2Impl, and Client2.
+        System.out.println("-------------------------- Modifying original service properties.");
+        m_changeStep = new Ensure();
+        for (int i = 1; i <= ASPECTS+1; i ++) {
+            m_changeStep.step(i); // skip all aspects and the adapter
+        }
+        props = new Hashtable();
+        props.put("foo", "barModified");
+        s.setServiceProperties(props);
+        
+        // Check if Client2 has been called in its "changed" callback
+        m_changeStep.waitForStep(ASPECTS+2, 5000);
+        
+        // Check if modified "foo" original service property has been propagated to Client2
+        Map check = new HashMap();
+        check.put("foo", "barModified");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS);
+        checkServiceProperties(check, client2Impl.getServiceProperties());
+        
+        // Clear all components.
+        m_changeStep = null;
+        m.clear();
+    }    
+    
+   private void checkServiceProperties(Map<?, ?> check, Dictionary properties) {
+        for (Object key : check.keySet()) {
+            Object val = check.get(key);   
+            if (val == null) {
+                Assert.assertNull(properties.get(key));
+            } else {
+                Assert.assertEquals(val, properties.get(key));
+            }
+        }
+    }
+    
+    private int getRandomAspect() {
+        int index = 0;  
+        do {
+            index = _rnd.nextInt(ASPECTS);            
+        } while (_randoms.contains(new Integer(index)));
+        _randoms.add(new Integer(index));
+        return index;
+    }
+
+    // S Service
+    public static interface S {
+        public void invoke();
+    }
+    
+    // S ServiceImpl
+    static class SImpl implements S {
+        public SImpl() {
+        }
+        
+        public String toString() {
+            return "S";
+        }
+        
+        public void invoke() {
+             m_invokeStep.step(ASPECTS+1);
+        }
+    }
+    
+    // S Aspect
+    static class A implements S {
+        private final String m_name;
+        private volatile ServiceRegistration m_registration;
+        private volatile S m_next;
+        private final int m_rank;
+
+        public A(String name, int rank) {
+            m_name = name;
+            m_rank = rank;
+        }
+        
+        public String toString() {
+            return m_name;
+        }
+        
+        public void invoke() {
+            int rank = ServiceUtil.getRanking(m_registration.getReference());
+            m_invokeStep.step(ASPECTS - rank + 1);
+            m_next.invoke();
+        }
+               
+        public void add(ServiceReference ref, S s) {
+            System.out.println("+++ A" + m_rank + ".add:" + s + "/" + ServiceUtil.toString(ref));   
+            m_next = s;
+        }
+
+        public void swap(ServiceReference oldSRef, S oldS, ServiceReference newSRef, S newS) {
+            System.out.println("+++ A" + m_rank + ".swap: new=" + newS + ", props=" + ServiceUtil.toString(newSRef));
+            Assert.assertTrue(m_next == oldS);
+            m_next = newS;
+        }
+
+        public void change(ServiceReference props, S s) {   
+            System.out.println("+++ A" + m_rank + ".change: s=" + s + ", props=" + ServiceUtil.toString(props));
+            if (m_changeStep != null) {
+                int rank = ServiceUtil.getRanking(m_registration.getReference());
+                m_changeStep.step(rank);
+            }
+        }
+        
+        public void remove(ServiceReference props, S s) {
+            System.out.println("+++ A" + m_rank + ".remove: " + s + ", props=" + ServiceUtil.toString(props));
+        }
+    }
+    
+    // Aspect aware client, depending of "S" service aspects.
+    static class Client {
+        private volatile S m_s;
+        private volatile ServiceReference m_sRef;
+
+        public Client() {
+        }
+        
+        public Dictionary getServiceProperties() {
+            Dictionary props = new Hashtable();
+            for (String key : m_sRef.getPropertyKeys()) {
+                props.put(key, m_sRef.getProperty(key));
+            }
+            return props;
+        }
+
+        public void invoke() {
+            m_s.invoke();           
+        }
+
+        public String toString() {
+            return "Client";
+        }
+        
+        public void add(ServiceReference ref, S s) {
+            System.out.println("+++ Client.add: " + s + "/" + ServiceUtil.toString(ref));
+            m_s = s;
+            m_sRef = ref;
+        }
+              
+        public void swap(ServiceReference oldSRef, S oldS, ServiceReference newSRef, S newS) {
+            System.out.println("+++ Client.swap: m_s = " + m_s + ", old=" + oldS + ", oldProps=" + ServiceUtil.toString(oldSRef) + ", new=" + newS + ", props=" + ServiceUtil.toString(newSRef));
+            Assert.assertTrue(m_s == oldS);
+            m_s = newS;
+            m_sRef = newSRef;
+        }
+
+        public void change(ServiceReference properties, S s) {
+            System.out.println("+++ Client.change: s=" + s + ", props=" + ServiceUtil.toString(properties));
+            if (m_changeStep != null) {
+                m_changeStep.step(ASPECTS+1);
+            }
+        }
+        
+        public void remove(ServiceReference props, S s) {
+            System.out.println("+++ Client.remove: " + s + ", props=" + ServiceUtil.toString(props));
+        }
+    }
+    
+    // S2 Service
+    public static interface S2 {
+        public void invoke2();
+    }
+
+    // S2 impl, which adapts S1 interface to S2 interface
+    static class S2Impl implements S2 {
+        private volatile S m_s; // we shall see top-level aspect on S service
+        
+        public void add(ServiceReference ref, S s) {
+            System.out.println("+++ S2Impl.add: " + s + "/" + ServiceUtil.toString(ref));
+            m_s = s;
+        }
+              
+        public void swap(ServiceReference oldSRef, S oldS, ServiceReference newSRef, S newS) {
+            System.out.println("+++ S2Impl.swap: new=" + newS + ", props=" + ServiceUtil.toString(newSRef));
+            m_s = newS;
+        }
+
+        public void change(ServiceReference properties, S s) {
+            System.out.println("+++ S2Impl.change: s=" + s + ", props=" + ServiceUtil.toString(properties));
+            if (m_changeStep != null) {
+                m_changeStep.step(ASPECTS+1);
+            }
+        }
+        
+        public void remove(ServiceReference props, S s) {
+            System.out.println("+++ S2Impl.remove: " + s + ", props=" + ServiceUtil.toString(props));
+        }
+        
+        public void invoke2() {
+            m_s.invoke();
+            m_invokeStep.step(ASPECTS + 2); // All aspects, and S1Impl have been invoked
+        }
+
+        public String toString() {
+            return "S2";
+        }        
+    }
+    
+    // Client2 depending on S2.
+    static class Client2 {
+        private volatile S2 m_s2;
+        private volatile ServiceReference m_s2Ref;
+
+        public Dictionary getServiceProperties() {
+            Dictionary props = new Hashtable();
+            for (String key : m_s2Ref.getPropertyKeys()) {
+                props.put(key, m_s2Ref.getProperty(key));
+            }
+            return props;
+        }
+
+        public void invoke2() {
+            m_s2.invoke2();           
+        }
+
+        public String toString() {
+            return "Client2";
+        }  
+                
+        public void add(ServiceReference ref, S2 s2) {
+            System.out.println("+++ Client2.add: " + s2 + "/" + ServiceUtil.toString(ref));
+            m_s2 = s2;
+            m_s2Ref = ref;
+        }
+
+        public void change(ServiceReference props, S2 s2) {   
+            System.out.println("+++ Client2.change: s2=" + s2 + ", props=" + ServiceUtil.toString(props));
+            if (m_changeStep != null) {
+                m_changeStep.step(ASPECTS + 2); // S1Impl, all aspects, and S2 adapters have been changed before us.
+            }
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AutoConfigTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AutoConfigTest.java
new file mode 100644
index 0000000..0e2ca8f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/AutoConfigTest.java
@@ -0,0 +1,251 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Constants;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class AutoConfigTest extends TestBase {
+    private final Ensure m_ensure = new Ensure();
+
+    public void testField() throws Exception {
+        final DependencyManager dm = getDM();
+        // Create a consumer, depending on some providers (autoconfig field).
+        ConsumeWithProviderField consumer = new ConsumeWithProviderField();
+        Component c = createConsumer(dm, consumer);
+        // Create two providers
+        Component p1 = createProvider(dm, 10, new Provider() {
+            public String toString() { return "provider1"; }
+            public void run() { m_ensure.step(); }
+        });
+        Component p2 = createProvider(dm, 20, new Provider() {
+            public String toString() { return "provider2"; }
+            public void run() { m_ensure.step(); }
+        });
+
+        // add the two providers
+        dm.add(p2);
+        dm.add(p1);
+        // add the consumer, which should have been injected with provider2 (highest rank)
+        dm.add(c);
+        m_ensure.waitForStep(1, 5000);
+        // remove the provider2, the consumer should now be injected with provider1
+        dm.remove(p2);
+        Assert.assertNotNull(consumer.getProvider());
+        Assert.assertEquals("provider1", consumer.getProvider().toString());
+        // remove the provider1, the consumer should have been stopped
+        dm.remove(p1);
+        m_ensure.waitForStep(2, 5000);
+        dm.clear();
+    }
+    
+    public void testIterableField() throws Exception {
+        final DependencyManager dm = getDM();
+        ConsumerWithIterableField consumer = new ConsumerWithIterableField();
+        Component c = createConsumer(dm, consumer);
+        Component p1 = createProvider(dm, 10, new Provider() {
+            public void run() { m_ensure.step(); }
+            public String toString() { return "provider1"; }
+        });
+        Component p2 = createProvider(dm, 20, new Provider() {
+            public void run() { m_ensure.step();}
+            public String toString() { return "provider2"; }
+        });
+
+        dm.add(p2);
+        dm.add(p1);
+        dm.add(c);
+        // the consumer should have been injected with all providers.
+        m_ensure.waitForStep(3, 5000);
+        
+        // check if all providers are there
+        Assert.assertNotNull(consumer.getProvider("provider1"));
+        Assert.assertNotNull(consumer.getProvider("provider2"));
+        
+        // remove provider1
+        dm.remove(p1);
+        
+        // check if provider1 has been removed and if provider2 is still there
+        Assert.assertNull(consumer.getProvider("provider1"));
+        Assert.assertNotNull(consumer.getProvider("provider2"));
+
+        // remove provider2, the consumer should be stopped
+        dm.remove(p2);
+        m_ensure.waitForStep(4, 5000);
+        dm.clear();
+    }   
+    
+    public void testMapField() throws Exception {
+        final DependencyManager dm = getDM();
+        ConsumerWithMapField consumer = new ConsumerWithMapField();
+        Component c = createConsumer(dm, consumer);
+        Component p1 = createProvider(dm, 10, new Provider() {
+            public void run() { m_ensure.step(); }
+            public String toString() { return "provider1"; }
+        });
+        Component p2 = createProvider(dm, 20, new Provider() {
+            public void run() { m_ensure.step();}
+            public String toString() { return "provider2"; }
+        });
+
+        dm.add(p2);
+        dm.add(p1);
+        dm.add(c);
+        // the consumer should have been injected with all providers.
+        m_ensure.waitForStep(3, 5000);
+        
+        // check if all providers are there
+        Assert.assertNotNull(consumer.getProvider("provider1"));
+        Assert.assertNotNull(consumer.getProvider("provider2"));
+        
+        // remove provider1
+        dm.remove(p1);
+        
+        // check if provider1 has been removed and if provider2 is still there
+        Assert.assertNull(consumer.getProvider("provider1"));
+        Assert.assertNotNull(consumer.getProvider("provider2"));
+
+        // remove provider2, the consumer should be stopped
+        dm.remove(p2);
+        m_ensure.waitForStep(4, 5000);
+        dm.clear();
+    }
+
+    private Component createProvider(DependencyManager dm, int rank, Provider provider) {
+        Hashtable props = new Hashtable();
+        props.put(Constants.SERVICE_RANKING, new Integer(rank));
+        return dm.createComponent()
+            .setImplementation(provider)
+            .setInterface(Provider.class.getName(), props);
+    }
+
+    private Component createConsumer(DependencyManager dm, Object consumer) {
+        return dm.createComponent()
+            .setImplementation(consumer)
+            .add(dm.createServiceDependency().setService(Provider.class).setRequired(true));
+    }
+
+    public static interface Provider extends Runnable {      
+    }
+    
+    public class ConsumeWithProviderField {
+        volatile Provider m_provider;
+        
+        void start() {
+            Assert.assertNotNull(m_provider);
+            Assert.assertEquals("provider2", m_provider.toString());
+            m_ensure.step(1);
+        }
+        
+        public Provider getProvider() {
+            return m_provider;
+        }
+
+        void stop() {
+            m_ensure.step(2);
+        }
+    }
+    
+    public class ConsumerWithIterableField {
+        final Iterable<Provider> m_providers = new ConcurrentLinkedQueue<>();
+        
+        void start() {
+            Assert.assertNotNull(m_providers);
+            int found = 0;
+            for (Provider provider : m_providers) {
+                provider.run();
+                found ++;
+            }
+            Assert.assertTrue(found == 2);
+            m_ensure.step(3);
+        }
+        
+        public Provider getProvider(String name) {
+            System.out.println("getProvider(" + name + ") : proviers=" + m_providers);
+            for (Provider provider : m_providers) {
+                if (provider.toString().equals(name)) {
+                    return provider;
+                }
+            }
+            return null;
+        }
+        
+        void stop() {
+            m_ensure.step(4);
+        }
+    }    
+    
+    public class ConsumerWithMapField {
+        final Map<Provider, Dictionary> m_providers = new ConcurrentHashMap<>();
+        
+        void start() {
+            Assert.assertNotNull(m_providers);
+            System.out.println("ConsumerMap.start: injected providers=" + m_providers);
+            Assert.assertTrue(m_providers.size() == 2);
+            for (Map.Entry<Provider, Dictionary> e : m_providers.entrySet()) {
+                Provider provider = e.getKey();
+                Dictionary props = e.getValue();
+                
+                provider.run();
+                if (provider.toString().equals("provider1")) {
+                    Assert.assertEquals(props.get(Constants.SERVICE_RANKING), 10);
+                } else if (provider.toString().equals("provider2")) {
+                    Assert.assertEquals(props.get(Constants.SERVICE_RANKING), 20);
+                } else {
+                    Assert.fail("Did not find any properties for provider " + provider);
+                }
+            }
+            
+            m_ensure.step(3);
+        }
+        
+        public Provider getProvider(String name) {
+            System.out.println("getProvider(" + name + ") : providers=" + m_providers);
+            for (Provider provider : m_providers.keySet()) {
+                if (provider.toString().equals(name)) {
+                    return provider;
+                }
+            }
+            return null;
+        }
+
+        Map<Provider, Dictionary> getProviders() {
+            return m_providers;
+        }
+        
+        void stop() {
+            m_ensure.step(4);
+        }
+    }    
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/BundleAdapterTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/BundleAdapterTest.java
new file mode 100644
index 0000000..9cd074a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/BundleAdapterTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.dm.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Bundle;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BundleAdapterTest extends TestBase {    
+    public void testBundleAdapter() {
+        DependencyManager m = getDM();
+        // create a bundle adapter service (one is created for each bundle)
+        Component adapter = m.createBundleAdapterService(Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE, null, false)
+                             .setImplementation(BundleAdapter.class)
+                             .setInterface(BundleAdapter.class.getName(), null);
+
+        // create a service provider and consumer
+        Consumer c = new Consumer();
+        Component consumer = m.createComponent().setImplementation(c)
+            .add(m.createServiceDependency().setService(BundleAdapter.class).setCallbacks("add", "remove"));
+        
+        // add the bundle adapter
+        m.add(adapter);
+        // add the service consumer
+        m.add(consumer);
+        // check if at least one bundle was found
+        c.check();
+        // remove the consumer again
+        m.remove(consumer);
+        // check if all bundles were removed correctly
+        c.doubleCheck();
+        // remove the bundle adapter
+        m.remove(adapter);
+    }
+        
+    public static class BundleAdapter {
+        volatile Bundle m_bundle;
+        
+        Bundle getBundle() {
+            return m_bundle;
+        }
+    }
+    
+    static class Consumer {
+        private volatile int m_count = 0;
+
+        public void add(BundleAdapter ba) {
+            Bundle b = ba.getBundle();
+            System.out.println("Consumer.add(" + b.getSymbolicName() + ")");
+            Assert.assertNotNull("bundle instance must not be null", b);
+            m_count++;
+        }
+        
+        public void check() {
+            Assert.assertTrue("we should have found at least one bundle", m_count > 0);
+        }
+        
+        public void remove(BundleAdapter ba) {
+            Bundle b = ba.getBundle();
+            System.out.println("Consumer.remove(" + b.getSymbolicName() + ")");
+            m_count--;
+        }
+        
+        public void doubleCheck() {
+            Assert.assertEquals("all bundles we found should have been removed again", 0, m_count);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/BundleDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/BundleDependencyTest.java
new file mode 100644
index 0000000..f59d9b0
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/BundleDependencyTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.dm.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Bundle;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BundleDependencyTest extends TestBase {
+    private final static String BSN = "org.apache.felix.metatype";
+    
+    public void testBundleDependencies() {
+        DependencyManager m = getDM();
+        // create a service provider and consumer
+        Consumer c = new Consumer();
+        Component consumer = m.createComponent().setImplementation(c).add(m.createBundleDependency().setCallbacks("add", "remove"));
+        // add the service consumer
+        m.add(consumer);
+        // check if at least one bundle was found
+        c.check();
+        // remove the consumer again
+        m.remove(consumer);
+        // check if all bundles were removed correctly
+        c.doubleCheck();
+        
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        Component consumerWithFilter = m.createComponent().setImplementation(new FilteredConsumer(e)).add(m.createBundleDependency().setFilter("(Bundle-SymbolicName=" + BSN + ")").setCallbacks("add", "remove"));
+        // add a consumer with a filter
+        m.add(consumerWithFilter);
+        e.step(2);
+        // remove the consumer again
+        m.remove(consumerWithFilter);
+        e.step(4);
+    }
+    
+    public void testRequiredBundleDependency() {
+        DependencyManager m = getDM();
+        
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        Component consumerWithFilter = m.createComponent()
+            .setImplementation(new FilteredConsumerRequired(e))
+            .add(m.createBundleDependency()
+                .setRequired(true)
+                .setFilter("(Bundle-SymbolicName=" + BSN + ")")
+                .setCallbacks("add", "remove")
+                );
+        // add a consumer with a filter
+        m.add(consumerWithFilter);
+        e.waitForStep(1, 5000);
+        // remove the consumer again
+        m.remove(consumerWithFilter);
+        e.waitForStep(2, 5000);
+    }
+    
+    static class Consumer {
+        private volatile int m_count = 0;
+
+        public void add(Bundle b) {
+            System.out.println("Consumer.add(" + b.getSymbolicName() + ")");
+            Assert.assertNotNull("bundle instance must not be null", b);
+            m_count++;
+        }
+        
+        public void check() {
+            Assert.assertTrue("we should have found at least one bundle", m_count > 0);
+        }
+        
+        public void remove(Bundle b) {
+            System.out.println("Consumer.remove(" + b.getSymbolicName() + ")");
+            m_count--;
+        }
+        
+        public void doubleCheck() {
+            Assert.assertEquals("all bundles we found should have been removed again", 0, m_count);
+        }
+    }
+    
+    static class FilteredConsumer {
+        private final Ensure m_ensure;
+
+        public FilteredConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void add(Bundle b) {
+            m_ensure.step(1);
+        }
+        
+        public void remove(Bundle b) {
+            m_ensure.step(3);
+        }
+    }
+    
+    static class FilteredConsumerRequired {
+        private final Ensure m_ensure;
+
+        public FilteredConsumerRequired(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void add(Bundle b) {
+            System.out.println("Bundle is " + b);
+//            Assert.assertNotNull(b);
+            if (b.getSymbolicName().equals(BSN)) {
+                m_ensure.step(1);
+            }
+        }
+        
+        public void remove(Bundle b) {
+            Assert.assertNotNull(b);
+            if (b.getSymbolicName().equals(BSN)) {
+                m_ensure.step(2);
+            }
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ComponentTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ComponentTest.java
new file mode 100644
index 0000000..bb4ca74
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ComponentTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class ComponentTest extends TestBase {
+    private final Ensure m_ensure = new Ensure();
+
+    public void testSimple() throws Exception {
+        final DependencyManager dm = getDM();
+        Component consumer = dm.createComponent();
+        consumer
+            .setImplementation(new Consumer())
+            .add(dm.createServiceDependency()
+                .setService(Provider.class, "(name=provider2)")
+                .setRequired(true)
+                .setCallbacks("add", "remove"))
+            .add(dm.createServiceDependency()
+                .setService(Provider.class, "(name=provider1)")
+                .setRequired(true)
+                .setAutoConfig("m_autoConfiguredProvider"));
+        
+        Dictionary props = new Hashtable();
+        props.put("name", "provider1");
+        Component provider1 = dm.createComponent()
+        		.setImplementation(new Provider() { public String toString() { return "provider1";}})
+        		.setInterface(Provider.class.getName(), props);
+        props = new Hashtable();
+        props.put("name", "provider2");
+        Component provider2 = dm.createComponent()
+				   .setImplementation(new Provider() { public String toString() { return "provider2";}})
+				   .setInterface(Provider.class.getName(), props);
+        dm.add(provider1);
+        dm.add(provider2);
+        dm.add(consumer);
+        m_ensure.waitForStep(2, 5000);
+        dm.remove(provider1); 
+        dm.remove(provider2);    
+        m_ensure.waitForStep(5, 5000);
+        dm.clear();
+    }
+    
+    public static interface Provider {    	
+    }
+    
+    public class Consumer {
+        Provider m_provider;
+        Provider m_autoConfiguredProvider;
+        
+        void add(Map props, Provider provider) {
+            Assert.assertNotNull(provider);
+            Assert.assertEquals("provider2", props.get("name"));
+            m_provider = provider;
+            m_ensure.step(1);
+        }
+        
+        void start() {
+            Assert.assertNotNull(m_autoConfiguredProvider);
+            Assert.assertEquals("provider1", m_autoConfiguredProvider.toString());
+            m_ensure.step(2);
+        }
+        
+        void stop() {
+            m_ensure.step(3);
+        }
+        
+        void destroy() {
+            m_ensure.step(4);
+        }
+        
+        void remove(Provider provider) {
+            Assert.assertEquals(m_provider, provider);
+            m_ensure.step(5);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ComponentTracker.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ComponentTracker.java
new file mode 100644
index 0000000..27d0b37
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ComponentTracker.java
@@ -0,0 +1,66 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentState;
+import org.apache.felix.dm.ComponentStateListener;
+
+/**
+ * Helper class used to wait for a group of components to be started and stopped.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ComponentTracker implements ComponentStateListener {
+    
+    private final CountDownLatch m_startLatch;
+    private final CountDownLatch m_stopLatch;
+
+    public ComponentTracker(int startCount, int stopCount) {
+        m_startLatch = new CountDownLatch(startCount);
+        m_stopLatch = new CountDownLatch(stopCount);
+    }
+
+    @Override
+    public void changed(Component c, ComponentState state) {
+        switch (state) {
+        case TRACKING_OPTIONAL:
+            m_startLatch.countDown();
+            break;
+            
+        case INACTIVE:
+            m_stopLatch.countDown();
+            break;
+        
+        default:
+        }
+    }
+    
+    public boolean awaitStarted(long millis) throws InterruptedException {
+        return m_startLatch.await(millis, TimeUnit.MILLISECONDS);
+    }
+    
+    public boolean awaitStopped(long millis) throws InterruptedException {
+        return m_stopLatch.await(millis, TimeUnit.MILLISECONDS);
+    }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/CompositionTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/CompositionTest.java
new file mode 100644
index 0000000..bfdcdd1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/CompositionTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.dm.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositionTest extends TestBase {
+    public void testComposition() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sc = m.createComponent().setImplementation(new ServiceConsumer(e))
+                                      .setComposition("getComposition")
+                                      .add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", null));
+        m.add(sp);
+        m.add(sc);
+        // ensure we executed all steps inside the component instance
+        e.step(6);
+        m.clear();
+    }
+    
+    static interface ServiceInterface {
+        public void invoke();
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke() {
+            m_ensure.step(4);
+        }
+    }
+
+    static class ServiceConsumer {
+        private final Ensure m_ensure;
+        private ServiceConsumerComposite m_composite;
+        @SuppressWarnings("unused")
+        private ServiceInterface m_service;
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+            m_composite = new ServiceConsumerComposite(m_ensure);
+        }
+        
+        public Object[] getComposition() {
+            return new Object[] { this, m_composite };
+        }
+        
+        void add(ServiceInterface service) {
+            m_ensure.step(1);
+            m_service = service; // This method seems to not being called anymore 
+        }
+        
+        void start() {
+            m_composite.invoke();
+            m_ensure.step(5); 
+        }
+    }
+    
+    static class ServiceConsumerComposite {
+        ServiceInterface m_service;
+        private Ensure m_ensure;
+        
+        ServiceConsumerComposite(Ensure ensure)
+        {
+            m_ensure = ensure;
+        }
+
+        void add(ServiceInterface service) {
+
+            m_ensure.step(2);
+            m_service = service;
+        }
+
+        void invoke()
+        {
+            m_ensure.step(3);
+            m_service.invoke();
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ConfigurationDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ConfigurationDependencyTest.java
new file mode 100644
index 0000000..d3a6aba
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ConfigurationDependencyTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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.dm.itest.api;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class ConfigurationDependencyTest extends TestBase {
+    final static String PID = "ConfigurationDependencyTest.pid";
+    
+    public void testComponentWithRequiredConfigurationAndServicePropertyPropagation() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Component s1 = m.createComponent().setImplementation(new ConfigurationConsumer(e)).setInterface(Runnable.class.getName(), null).add(m.createConfigurationDependency().setPid(PID).setPropagate(true));
+        Component s2 = m.createComponent().setImplementation(new ConfigurationCreator(e)).add(m.createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true));
+        Component s3 = m.createComponent().setImplementation(new ConfiguredServiceConsumer(e)).add(m.createServiceDependency().setService(Runnable.class, ("(testkey=testvalue)")).setRequired(true));
+        m.add(s1);
+        m.add(s2);
+        m.add(s3);
+        e.waitForStep(4, 50000000);
+        m.remove(s1);
+        m.remove(s2);
+        m.remove(s3);
+        // ensure we executed all steps inside the component instance
+        e.step(6);
+    }
+    
+    public void testComponentWithRequiredConfigurationAndCallbackInstanceAndServicePropertyPropagation() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        ConfigurationConsumerCallbackInstance callbackInstance = new ConfigurationConsumerCallbackInstance(e);
+        Component s1 = m.createComponent().setImplementation(new ConfigurationConsumerWithCallbackInstance(e))
+            .setInterface(Runnable.class.getName(), null)
+            .add(m.createConfigurationDependency().setPid(PID).setPropagate(true).setCallback(callbackInstance, "updateConfiguration"));
+        Component s2 = m.createComponent().setImplementation(new ConfigurationCreator(e))
+            .add(m.createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true));
+        Component s3 = m.createComponent().setImplementation(new ConfiguredServiceConsumer(e))
+            .add(m.createServiceDependency().setService(Runnable.class, ("(testkey=testvalue)")).setRequired(true));
+        m.add(s1);
+        m.add(s2);
+        m.add(s3);
+        e.waitForStep(4, 5000);
+        m.remove(s1);
+        m.remove(s2);
+        m.remove(s3);
+        // ensure we executed all steps inside the component instance
+        e.step(6);
+    }
+    
+    public void testFELIX2987() {
+        // mimics testComponentWithRequiredConfigurationAndServicePropertyPropagation
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Component s1 = m.createComponent().setImplementation(new ConfigurationConsumer2(e)).setInterface(Runnable.class.getName(), null).add(m.createConfigurationDependency().setPid(PID).setPropagate(true));
+        Component s2 = m.createComponent().setImplementation(new ConfigurationCreator(e)).add(m.createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true));
+        Component s3 = m.createComponent().setImplementation(new ConfiguredServiceConsumer(e)).add(m.createServiceDependency().setService(Runnable.class, ("(testkey=testvalue)")).setRequired(true));
+        m.add(s1);
+        m.add(s2);
+        m.add(s3);
+        e.waitForStep(4, 5000);
+        m.remove(s1);
+        m.remove(s2);
+        m.remove(s3);
+        // ensure we executed all steps inside the component instance
+        e.step(6);
+    }
+
+
+    class ConfigurationCreator {
+        private volatile ConfigurationAdmin m_ca;
+        private final Ensure m_ensure;
+        Configuration m_conf;
+        
+        public ConfigurationCreator(Ensure e) {
+            m_ensure = e;
+        }
+
+        public void init() {
+            try {
+                warn("ConfigurationCreator.init");
+            	Assert.assertNotNull(m_ca);
+                m_ensure.step(1);
+                m_conf = m_ca.getConfiguration(PID, null);
+                Hashtable props = new Properties();
+                props.put("testkey", "testvalue");
+                m_conf.update(props);
+            }
+            catch (IOException e) {
+                Assert.fail("Could not create configuration: " + e.getMessage());
+            }
+        }
+        
+        public void destroy() throws IOException {
+            warn("ConfigurationCreator.destroy");
+        	m_conf.delete();  
+        	m_ensure.step();
+        }
+    }
+    
+    static class ConfigurationConsumer2 extends ConfigurationConsumer {
+        public ConfigurationConsumer2(Ensure e) {
+            super(e);
+        }
+    }
+
+    static class ConfigurationConsumer implements ManagedService, Runnable {
+        private final Ensure m_ensure;
+
+        public ConfigurationConsumer(Ensure e) {
+            m_ensure = e;
+        }
+
+        public void updated(Dictionary props) throws ConfigurationException {
+            if (props != null) {
+                m_ensure.step(2);
+                if (!"testvalue".equals(props.get("testkey"))) {
+                    Assert.fail("Could not find the configured property.");
+                }
+            }
+        }
+        
+        public void run() {
+            m_ensure.step(4);
+        }
+    }
+    
+    static class ConfigurationConsumerCallbackInstance {
+        private final Ensure m_ensure;
+
+        public ConfigurationConsumerCallbackInstance(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void updateConfiguration(Dictionary props) throws Exception {
+            if (props != null) {
+                m_ensure.step(2);
+                if (!"testvalue".equals(props.get("testkey"))) {
+                    Assert.fail("Could not find the configured property.");
+                }
+            } 
+        }
+    }
+    
+    static class ConfigurationConsumerWithCallbackInstance implements Runnable {
+        private final Ensure m_ensure;
+
+        public ConfigurationConsumerWithCallbackInstance(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void run() {
+            m_ensure.step(4);
+        }
+    }
+
+    static class ConfiguredServiceConsumer {
+        private final Ensure m_ensure;
+        private volatile Runnable m_runnable;
+
+        public ConfiguredServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            m_ensure.step(3);
+            m_runnable.run();
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/DynamicProxyAspectTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/DynamicProxyAspectTest.java
new file mode 100644
index 0000000..b5e5938
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/DynamicProxyAspectTest.java
@@ -0,0 +1,199 @@
+/**
+ * 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.dm.itest.api;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"rawtypes"})
+public class DynamicProxyAspectTest extends TestBase {
+    public void testImplementGenericAspectWithDynamicProxy() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        
+        // create two service providers, each providing a different service interface
+        Component sp1 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sp2 = m.createComponent().setImplementation(new ServiceProvider2(e)).setInterface(ServiceInterface2.class.getName(), null);
+        
+        // create a dynamic proxy based aspect and hook it up to both services
+        Component a1 = m.createAspectService(ServiceInterface.class, null, 10, "m_service")
+            .setFactory(new Factory(e, ServiceInterface.class, "ServiceInterfaceProxy"), "create");
+        Component a2 = m.createAspectService(ServiceInterface2.class, null, 10, "m_service")
+            .setFactory(new Factory(e, ServiceInterface2.class, "ServiceInterfaceProxy2"), "create");
+
+        // create a client that invokes a method on boths services, validate that it goes
+        // through the proxy twice
+        Component sc = m.createComponent()
+            .setImplementation(new ServiceConsumer(e))
+            .add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true))
+            .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(true))
+            ;
+        
+        // register both producers, validate that both services are started
+        m.add(sp1);
+        e.waitForStep(1, 2000);
+        m.add(sp2);
+        e.waitForStep(2, 2000);
+        
+        // add both aspects, and validate that both instances have been created
+        m.add(a1);
+        m.add(a2);
+        e.waitForStep(4, 4000);
+        
+        // add the client, which will automatically invoke both services
+        m.add(sc);
+        
+        // wait until both services have been invoked
+        e.waitForStep(6, 4000);
+        
+        // make sure the proxy has been called twice
+        Assert.assertEquals("Proxy should have been invoked this many times.", 2, DynamicProxyHandler.getCounter());
+        
+        m.remove(sc);
+        m.remove(a2);
+        m.remove(a1);
+        m.remove(sp2);
+        m.remove(sp1);
+        m.remove(a2);
+        m.remove(a1);
+        
+        try {
+            Thread.sleep(2000);
+        }
+        catch (InterruptedException e1) {
+            // TODO Auto-generated catch block
+            e1.printStackTrace();
+        }
+    }
+    
+    static interface ServiceInterface {
+        public void invoke(Runnable run);
+    }
+    
+    static interface ServiceInterface2 {
+        public void invoke(Runnable run);
+    }
+    
+    static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void start() {
+            m_ensure.step(1);
+        }
+        public void invoke(Runnable run) {
+            run.run();
+        }
+    }
+    
+    static class ServiceProvider2 implements ServiceInterface2 {
+        private final Ensure m_ensure;
+        public ServiceProvider2(Ensure ensure) {
+            m_ensure = ensure;
+        }
+        public void start() {
+            m_ensure.step(2);
+        }
+        public void invoke(Runnable run) {
+            run.run();
+        }
+    }
+    
+    static class ServiceConsumer implements Runnable {
+        private volatile ServiceInterface m_service;
+        private volatile ServiceInterface2 m_service2;
+        private final Ensure m_ensure;
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            Thread t = new Thread(this);
+            t.start();
+        }
+        
+        public void run() {
+            m_service.invoke(Ensure.createRunnableStep(m_ensure, 5));
+            m_service2.invoke(Ensure.createRunnableStep(m_ensure, 6));
+        }
+    }
+    
+    static class DynamicProxyHandler implements InvocationHandler {
+        public volatile Object m_service; // ISSUE, we cannot inject into "Object" at the moment
+        private final String m_label;
+        private static volatile int m_counter = 0;
+
+        public DynamicProxyHandler(String label) {
+            m_label = label;
+        }
+
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            System.out.println("IIIIIIINVOKE--------------------------" + method.getName());
+            if (m_service == null) {
+                Assert.fail("No service was injected into dynamic proxy handler " + m_label);
+            }
+            Method m = m_service.getClass().getMethod(method.getName(), method.getParameterTypes());
+            if (m == null) {
+                Assert.fail("No method " + method.getName() + " was found in instance " + m_service + " in dynamic proxy handler " + m_label);
+            }
+            if (method.getName().equals("invoke")) {
+                // only count methods called 'invoke' because those are actually the ones
+                // both interfaces implement (and the dynamic proxy might be invoked for
+                // other methods, such as toString() as well)
+                m_counter++;
+            }
+            return m.invoke(m_service, args);
+        }
+        
+        public static int getCounter() {
+            return m_counter;
+        }
+    }
+    
+    static class Factory {
+        private final String m_label;
+        private Class m_class;
+        private final Ensure m_ensure;
+        
+        public Factory(Ensure ensure, Class clazz, String label) {
+            m_ensure = ensure;
+            m_class = clazz;
+            m_label = label;
+        }
+        
+        public Object create() {
+            m_ensure.step();
+            return Proxy.newProxyInstance(m_class.getClassLoader(), new Class[] { m_class }, new DynamicProxyHandler(m_label));
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2078_ServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2078_ServiceDependencyTest.java
new file mode 100644
index 0000000..030bd0b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2078_ServiceDependencyTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.dm.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX2078_ServiceDependencyTest extends TestBase {
+    public void testRequiredServiceRegistrationAndConsumption() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sp2 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", "remove"));
+        m.add(sp);
+        m.add(sp2);
+        System.out.println("adding client");
+        m.add(sc);
+        System.out.println("waiting");
+        // wait until both services have been added to our consumer
+        e.waitForStep(2, 5000);
+        m.remove(sc);
+        m.remove(sp2);
+        m.remove(sp);
+        // ensure we executed all steps inside the component instance
+    }
+    
+    public void testOptionalServiceRegistrationAndConsumption() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sp2 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(false).setCallbacks("add", "remove"));
+        m.add(sp);
+        m.add(sp2);
+        m.add(sc);
+        // wait until both services have been added to our consumer
+        e.waitForStep(2, 5000);
+        m.remove(sc);
+        m.remove(sp2);
+        m.remove(sp);
+        // ensure we executed all steps inside the component instance
+    }
+    
+    static interface ServiceInterface {
+        public void invoke();
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        public ServiceProvider(Ensure e) {
+        }
+        public void invoke() {
+        }
+    }
+
+    static class ServiceConsumer {
+        private final Ensure m_ensure;
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void add(ServiceInterface i) {
+        	System.out.println("add " + i);
+            m_ensure.step();
+        }
+        
+        public void remove(ServiceInterface i) {
+            
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2344_ExtraDependencyWithAutoConfigTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2344_ExtraDependencyWithAutoConfigTest.java
new file mode 100644
index 0000000..b87f7d1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2344_ExtraDependencyWithAutoConfigTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.dm.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings("unused")
+public class FELIX2344_ExtraDependencyWithAutoConfigTest extends TestBase {
+    /**
+     * Test if an auto config extra dependency is injected in the expected order.
+     */
+    public void testExtraDependencyWithAutoConfig() {  
+        DependencyManager m = getDM();
+        // Helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // Create a service provider
+        Component sp = m.createComponent().setInterface(ProviderInterface.class.getName(), null).setImplementation(ProviderImpl.class);
+        // Create a service consumer with a required/autoconfig dependency over the service provider.
+        Client c1;
+        Component sc1 = m.createComponent().setImplementation((c1 = new Client(e, true, 1)));
+        // Create a second service consumer with an optional/autoconfig dependency over the service provider.
+        Client c2;
+        Component sc2 = m.createComponent().setImplementation(c2 = new Client(e, false, 3));
+
+        // Add service provider and consumer sc1 (required dependency over provider)
+        m.add(sc1);
+        m.add(sp);
+        e.waitForStep(2, 5000); 
+        
+        // Remove provider and consumer
+        m.remove(sc1);
+        m.remove(sp);
+        
+        // Add consumer sc2 (optional dependency over provider)
+        m.add(sc2);
+        e.waitForStep(4, 5000);    
+        m.clear();
+    }
+
+    public interface ProviderInterface {
+        public boolean action();
+    }
+
+    public static class ProviderImpl implements ProviderInterface {
+        public boolean action()
+        {
+            return true;
+        }
+    }
+    
+    // This client is not using callbacks, but instead, it uses auto config.
+    public static class Client {
+        volatile ProviderInterface m_provider;
+        private Ensure m_ensure;
+        private final boolean m_required;
+        private final int m_startStep;
+
+        public Client(Ensure e, boolean required, int startStep) {
+            m_ensure = e;
+            m_required = required;
+            m_startStep = startStep;
+        }
+        
+        public void init(Component s) {
+            DependencyManager dm = s.getDependencyManager();
+            m_ensure.step(m_startStep);
+            s.add(dm.createServiceDependency()
+                .setService(ProviderInterface.class)
+                .setRequired(m_required)
+                .setAutoConfig("m_provider"));
+        }
+
+        public void start() {
+            // if required dependency: we must have been injected with the service provider
+            // else, we have been injected with a null object.
+            Assert.assertNotNull("provider has not been injected", m_provider);
+            Assert.assertEquals(m_required, m_provider.action()); // action returns false if null object
+            m_ensure.step();
+        } 
+    }
+}
+
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2344_ExtraDependencyWithCallbackTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2344_ExtraDependencyWithCallbackTest.java
new file mode 100644
index 0000000..a28c8ab
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2344_ExtraDependencyWithCallbackTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.dm.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * Tests for extra dependencies which are declared from service's init method.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX2344_ExtraDependencyWithCallbackTest extends TestBase {
+    /**
+     * Checks if an extra optional/required dependency is properly injected into a consumer, using callbacks.
+     */
+    public void testExtraDependencyWithCallback() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service consumer and provider
+        Component sp = m.createComponent().setInterface(ProviderInterface.class.getName(), null).setImplementation(ProviderImpl.class);
+        Component sc = m.createComponent().setImplementation(new Client(e, false, 1));
+        Component sc2 = m.createComponent().setImplementation(new Client(e, true, 5));
+        Component sc3 = m.createComponent().setImplementation(new Client(e, true, 9));
+        
+        // add the provider first, then add the consumer which initially will have no dependencies
+        // but via the init() method an optional dependency with a callback method will be added
+        m.add(sp);
+        m.add(sc);
+        // remove the consumer again
+        m.remove(sc);
+        e.waitForStep(4, 5000);
+
+        // next up, add a second consumer, identical to the first, but with a required dependency
+        // with a callback method which will be added in the init() method
+        m.add(sc2);
+        // remove the consumer again
+        m.remove(sc2);
+        e.waitForStep(8, 5000);
+
+        // now remove the provider, add a third consumer, identical to the second, and after the
+        // consumer has started, add the provider again
+        m.remove(sp);
+        m.add(sc3);
+        m.add(sp);
+        e.waitForStep(12, 5000);
+        m.clear();
+    }
+    
+    public interface ProviderInterface {
+    }
+
+    public static class ProviderImpl implements ProviderInterface {
+    }
+
+    public static class Client {
+        ProviderInterface m_provider;
+        private Ensure m_ensure;
+        private final boolean m_required;
+        private final int m_startStep;
+
+        public Client(Ensure e, boolean required, int startStep) {
+            m_ensure = e;
+            m_required = required;
+            m_startStep = startStep;
+        }
+
+        public void init(Component s) {
+            DependencyManager dm = s.getDependencyManager();
+            m_ensure.step(m_startStep);
+            s.add(dm.createServiceDependency()
+                .setService(ProviderInterface.class)
+                .setRequired(m_required)
+                .setCallbacks("bind", null));
+        }
+
+        // called before start() for required dependency, or after start for optional dependency
+        void bind(ProviderInterface provider) {
+            System.out.println("bind");
+            m_ensure.step(m_required ? m_startStep + 1 : m_startStep + 3);
+            m_provider = provider;
+        }
+
+        public void start() {
+            System.out.println("start");
+            m_ensure.step(m_required ? m_startStep + 2: m_startStep + 1);
+            if (m_required) {
+                Assert.assertNotNull("Dependendency should have been injected", m_provider);
+            }
+            m_ensure.step(m_required ? m_startStep + 3: m_startStep + 2);
+        }
+    }    
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2348_ResourceAdapterTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2348_ResourceAdapterTest.java
new file mode 100644
index 0000000..fbf592d
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2348_ResourceAdapterTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.dm.itest.api;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.junit.Assert;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX2348_ResourceAdapterTest extends TestBase {
+    public void testBasicResourceAdapter() throws Exception {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        m.add(m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, null, "changed")
+              .setImplementation(new ResourceAdapter(e)));
+        URL resourceURL = new URL("file://localhost/path/to/file1.txt");
+        m.add(m.createComponent().setImplementation(new ResourceProvider(context, resourceURL))
+        	   .add(m.createServiceDependency().setService(ResourceHandler.class).setCallbacks("add", "remove")));
+        e.waitForStep(3, 5000);
+        m.clear();
+     }
+    
+    static class ResourceAdapter {
+        protected URL m_resource; // injected by reflection.
+        private Ensure m_ensure;
+        
+        ResourceAdapter(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void start() {
+            m_ensure.step(1);
+            Assert.assertNotNull("resource not injected", m_resource);
+            m_ensure.step(2);
+            try {
+                m_resource.openStream();
+            } 
+            catch (FileNotFoundException e) {
+                m_ensure.step(3);
+            }
+            catch (IOException e) {
+                Assert.fail("We should not have gotten this exception.");
+            }
+        }
+    }    
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2369_ExtraDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2369_ExtraDependencyTest.java
new file mode 100644
index 0000000..bb1ff01
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2369_ExtraDependencyTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.dm.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * This testcase verify that a Service is not started if one of its extra required dependencies
+ * is unavailable.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX2369_ExtraDependencyTest extends TestBase
+{
+    public void testExtraDependencies() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service consumer and provider
+        Component sp1 = m.createComponent().setInterface(MyService1.class.getName(), null).setImplementation(new MyService1Impl());
+        Component sc = m.createComponent().setImplementation(new MyClient(e, 1));
+        
+        // provides the MyService1 service (but not the MyService2, which is required by MyClient).
+        m.add(sp1);
+        // add MyClient (it should not be invoked in its start() method because MyService2 is not there
+        m.add(sc);
+        // remove MyClient (it should not be invoked in its stop() method because it should not be active, since MyService2 is not there.
+        m.remove(sc);
+        e.waitForStep(2, 5000);
+        m.clear();
+    }
+    
+    public interface MyService1 {
+    }
+    
+    public interface MyService2 {
+    }
+
+    public static class MyService1Impl implements MyService1 {
+    }
+
+    public static class MyService2Impl implements MyService2 {
+    }
+
+    // This client is not using callbacks, but instead, it uses auto config.
+    public static class MyClient {
+        MyService1 m_myService2; // required/unavailable      
+        private Ensure m_ensure;
+        private final int m_startStep;
+
+        public MyClient(Ensure e, int startStep) {
+            m_ensure = e;
+            m_startStep = startStep;
+        }
+        
+        public void init(Component s) {
+            DependencyManager dm = s.getDependencyManager();
+            m_ensure.step(m_startStep);
+            ServiceDependency d1 = 
+                    dm.createServiceDependency() // this dependency is available at this point
+                      .setService(MyService1.class)
+                      .setRequired(false)
+                      .setCallbacks("bind", null);
+            ServiceDependency d2 = 
+            		dm.createServiceDependency() // not available: we should not be started
+                      .setService(MyService2.class)
+                      .setRequired(true)
+                      .setAutoConfig("m_myService2");
+            
+            s.add(d1, d2); // atomically add these two dependencies
+        }
+
+        public void start() {
+            Assert.fail("start should not be called since MyService2 is unavailable");
+        }
+        
+        void bind(MyService1 s1) { // optional/available
+            System.out.println("bound MyService1");
+        }
+        
+        public void stop() {
+            Assert.fail("stop should not be called since we should not be active at this point");
+        }
+        
+        public void destroy() {
+            m_ensure.step(m_startStep+1);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2696_ConfigurationAndServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2696_ConfigurationAndServiceDependencyTest.java
new file mode 100644
index 0000000..6f2ea65
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2696_ConfigurationAndServiceDependencyTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.dm.itest.api;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings("rawtypes")
+public class FELIX2696_ConfigurationAndServiceDependencyTest extends TestBase {
+    final static String PID = "FELIX2696_ConfigurationAndServiceDependencyTest.pid";
+
+    public void testComponentWithRequiredConfigurationAndServicePropertyPropagation() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Component s1 = m.createComponent()
+            .setImplementation(new ConfigurationConsumer(e))
+            .add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true))
+            .add(m.createConfigurationDependency().setPid(PID));
+        Component s2 = m.createComponent()
+            .setImplementation(new ConfigurationCreator(e))
+            .add(m.createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true));
+        Component s3 = m.createComponent()
+            .setInterface(ServiceInterface.class.getName(), null)
+            .setImplementation(DependentServiceProvider.class);
+        m.add(s1);
+        m.add(s2);
+        m.add(s3);
+        e.waitForStep(2, 5000);
+        warn("removing s3");
+        m.remove(s3);
+        warn("readding s3");
+        m.add(s3);
+        // after adding the required dependency again, the issue in FELIX-2696 means that the
+        // updated() method is not invoked for the new instance, and init() is, so our step
+        // count will only go up to 3 (not 4) causing this test to fail
+        e.waitForStep(4, 5000);
+        m.remove(s3);
+        m.remove(s2);
+        m.remove(s1);
+        e.waitForStep(5, 5000);
+    }
+
+    public static class ConfigurationCreator {
+        private volatile ConfigurationAdmin m_ca;
+        private final Ensure m_ensure;
+        Configuration m_conf;
+        
+        public ConfigurationCreator(Ensure e) {
+            m_ensure = e;
+        }
+
+        @SuppressWarnings("unchecked")
+        public void init() {
+            try {
+                m_conf = m_ca.getConfiguration(PID, null);
+                Hashtable props = new Hashtable();
+                props.put("testkey", "testvalue");
+                m_conf.update(props);
+            }
+            catch (IOException e) {
+                Assert.fail("Could not create configuration: " + e.getMessage());
+            }
+        }
+        
+        public void destroy() throws IOException {
+        	m_conf.delete();
+        	m_ensure.step(5);
+        }
+    }
+
+    public class ConfigurationConsumer implements ManagedService {
+        private final Ensure m_ensure;
+
+        public ConfigurationConsumer(Ensure e) {
+            m_ensure = e;
+        }
+
+        public void updated(Dictionary props) throws ConfigurationException {
+        	warn("Consumer: updated %s", props);
+            if (props != null) {
+                if (!"testvalue".equals(props.get("testkey"))) {
+                    Assert.fail("Could not find the configured property.");
+                }
+                m_ensure.step();
+            }
+        }
+        
+        public void init() {
+        	warn("Consumer: init");
+            m_ensure.step();
+        }
+    }
+
+    public static interface ServiceInterface {
+    }
+    
+    public static class DependentServiceProvider implements ServiceInterface {
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2875_ServiceDependencyWithoutServiceNameTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2875_ServiceDependencyWithoutServiceNameTest.java
new file mode 100644
index 0000000..3cc2a90
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2875_ServiceDependencyWithoutServiceNameTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX2875_ServiceDependencyWithoutServiceNameTest extends TestBase {
+    Ensure m_e;
+    
+    public void testServiceDependencyWithoutName() {
+        m_e = new Ensure();
+        DependencyManager dm = getDM();
+        Component consumer = dm.createComponent()
+            .setImplementation(new ConsumeServiceDependencyWithoutName())
+            .add(dm.createServiceDependency()
+                .setService("(provider=*)").setRequired(true)
+                .setCallbacks("add", null))
+            .add(dm.createServiceDependency()
+                .setService("(|(provider=provider1)(provider=provider2))").setRequired(true)
+                .setAutoConfig("m_providers"));
+        
+        Hashtable<String, String> props = new Hashtable<>();
+        props.put("provider", "provider1");
+        Component provider1 = dm.createComponent()
+            .setImplementation(new Provider1())
+            .setInterface(Provider.class.getName(), props);
+            
+        props = new Hashtable<>();
+        props.put("provider", "provider2");
+        Component provider2 = dm.createComponent()
+            .setImplementation(new Provider2())
+            .setInterface(Provider.class.getName(), props);
+
+       dm.add(provider1);
+       dm.add(provider2);
+       dm.add(consumer);
+       m_e.waitForStep(5, 5000);    
+       dm.clear();
+    }
+    
+    private class ConsumeServiceDependencyWithoutName {
+        volatile Map<Object, Dictionary<String, String>> m_providers; // autoconfig
+
+        @SuppressWarnings("unused")
+        void add(Map<String, Object> props, Object service) {
+            if ("provider1".equals(props.get("provider"))) {
+                m_e.step();
+            } else if ("provider2".equals(props.get("provider"))) {
+                m_e.step();
+            }
+        }
+        
+        @SuppressWarnings("unused")
+        void start() {
+            // check if all providers have been injected in our autoconfig field.
+            for (Map.Entry<Object, Dictionary<String, String>> e : m_providers.entrySet()) {
+                if ("provider1".equals(e.getValue().get("provider"))) {
+                    m_e.step();
+                } else if ("provider2".equals(e.getValue().get("provider"))) {
+                    m_e.step();
+                }
+            }
+            m_e.step(5);
+        }
+    }
+    
+    public interface Provider {
+    }
+    
+    public class Provider1 implements Provider {
+    }
+    
+    public class Provider2 implements Provider {
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2955_ShellCommandTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2955_ShellCommandTest.java
new file mode 100644
index 0000000..4ee902a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX2955_ShellCommandTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.dm.itest.api;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.osgi.framework.Bundle;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX2955_ShellCommandTest extends TestBase {
+    private long m_myBundleId;
+    private Bundle m_testBundle;
+
+    public void testShellCommands() throws Throwable {
+    	try {
+    		m_myBundleId = context.getBundle().getBundleId();
+    		for (Bundle b : context.getBundles()) {
+    			if (b.getSymbolicName().equals("org.apache.felix.dependencymanager.itest.bundle")) {
+    				m_testBundle = b;
+    				b.stop();
+    				break;
+    			}
+    		}
+    		DependencyManager m = getDM();
+    		// helper class that ensures certain steps get executed in sequence
+    		Ensure e = new Ensure();
+        
+    		Component shellClient = m.createComponent();
+    		Component missing = m.createComponent();
+        
+    		long shellClientId = shellClient.getComponentDeclaration().getId();
+    		long missingId = missing.getComponentDeclaration().getId();
+    		shellClient.setImplementation(new ShellClient(e, shellClientId, missingId))
+    				   .add(m.createServiceDependency()
+    						   .setService(CommandProcessor.class)
+    						   .setRequired(true));
+            
+    		m.add(shellClient);
+    		e.waitForStep(3, 5000);
+    		// now create a component with a missing dependency
+    		missing.setImplementation(new Object() { public String toString() { return "Object"; }})
+    			   .add(m.createServiceDependency()
+    					 .setService(Missing.class) // Warning: don't use Object, or Runnable, which are already registered by bndtools ?
+    					 .setRequired(true));
+            
+    		m.add(missing);
+    		e.step(4);
+    		e.waitForStep(5, 5000);
+    		m.remove(missing);
+    		// now start/stop our test bundle, which publishes a service that uses the dependency manager
+    		m_testBundle.start();    		
+    		m_testBundle.stop();
+    		e.step(6);
+    		e.waitForStep(7, 5000);
+    		e.ensure();
+    		m.remove(shellClient);
+    		m_testBundle.start(); // restart the runtime bundle
+    		m.clear();
+    	} 
+    	
+    	catch (Throwable t) {
+    		error("test failed", t);
+    	}
+    }
+    
+    public class ShellClient {
+        volatile CommandProcessor m_commandProcessor;
+        private final Ensure m_ensure;
+        private final long m_shellClientId;
+        private final long m_missingId;
+        
+        public ShellClient(Ensure e, long shellClientId, long missingId) {
+            m_ensure = e;
+            m_shellClientId = shellClientId;
+            m_missingId = missingId;
+        }
+
+        public void start() throws InterruptedException {
+            Thread t = new Thread("Shell Client") {
+                public void run() {
+                    String bsn = context.getBundle().getSymbolicName();
+                    m_ensure.step(1);
+                    execute("dm bid " + m_myBundleId,
+                          "[" + m_myBundleId + "] " + bsn + "\n" +
+                          " [" + m_shellClientId + "] ShellClient registered\n" +
+                          "    org.apache.felix.service.command.CommandProcessor service required available\n", 
+                          "");
+                    
+                    m_ensure.step(2);
+                    // see if there's anything that's not available
+                    execute("dm notavail bid " + m_myBundleId,
+                        "", 
+                        "");
+                    m_ensure.step(3);
+                    // check again, now there should be something missing
+                    m_ensure.waitForStep(4, 5000);
+                    execute("dm notavail bid " + m_myBundleId,
+                            "[" + m_myBundleId + "] " + bsn + "\n" +
+                            " [" + m_missingId + "] Object unregistered\n" + 
+                            "    " + Missing.class.getName() + " service required unavailable\n", 
+                            "");
+                    m_ensure.step(5);
+                    m_ensure.waitForStep(6, 5000);
+                    // this next step actually triggers the bug in FELIX-2955
+                    execute("dm notavail bid " + m_myBundleId,
+                        "", 
+                        "");
+                    m_ensure.step(7);
+                }
+            };
+            t.start();
+        }
+        
+        @Override
+        public String toString() {
+            return "ShellClient";
+        }
+        
+        public void execute(String command, String expectedOutput, String expectedError) {
+            try {
+                ByteArrayOutputStream output = new ByteArrayOutputStream();
+                ByteArrayOutputStream error = new ByteArrayOutputStream();
+                CommandSession session = m_commandProcessor.createSession(System.in, new PrintStream(output), new PrintStream(error));
+                session.execute(command); 
+                
+                String out = output.toString();                                                                
+                Assert.assertEquals(expectedOutput, out.toString());
+                Assert.assertEquals(expectedError, error.toString());
+            }
+            catch (Throwable throwable) {
+                m_ensure.throwable(throwable);
+            }
+        }
+    }
+    
+    public static class Missing {       
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3008_FilterIndexStartupTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3008_FilterIndexStartupTest.java
new file mode 100644
index 0000000..f5b3f41
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3008_FilterIndexStartupTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.dm.itest.api;
+//import static org.ops4j.pax.exam.CoreOptions.waitForFrameworkStartupFor;
+//import static org.ops4j.pax.exam.container.def.PaxRunnerOptions.vmOption;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Bundle;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX3008_FilterIndexStartupTest extends TestBase {
+    public void testNormalStart() throws Exception {
+        System.setProperty("org.apache.felix.dependencymanager.filterindex", "objectClass");
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a provider
+        Provider provider = new Provider();
+        // activate it
+        Component p = m.createComponent()
+            .setInterface(Service.class.getName(), null)
+            .setImplementation(provider);
+        
+        Consumer consumer = new Consumer(e);
+        Component c = m.createComponent()
+            .setImplementation(consumer)
+            .add(m.createServiceDependency()
+                .setService(Service.class)
+                .setRequired(true)
+                );
+        
+        m.add(p);
+        m.add(c);
+        e.waitForStep(1, 5000);
+        m.remove(p);
+        e.waitForStep(2, 5000);
+        m.remove(c);
+        
+        Assert.assertEquals("Dependency manager bundle should be active.", Bundle.ACTIVE, context.getBundle().getState());
+    }
+
+    public static class Consumer {
+        volatile Service m_service;
+        private final Ensure m_ensure;
+        
+        public Consumer(Ensure e) {
+            m_ensure = e;
+        }
+
+        public void start() {
+            System.out.println("start");
+            m_ensure.step(1);
+        }
+        
+        public void stop() {
+            System.out.println("stop");
+            m_ensure.step(2);
+        }
+    }
+    
+    public static interface Service {
+    }
+    
+    public static class Provider implements Service {
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3057_EmptyServiceReferenceArray.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3057_EmptyServiceReferenceArray.java
new file mode 100644
index 0000000..07ce09b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3057_EmptyServiceReferenceArray.java
@@ -0,0 +1,52 @@
+/*
+ * 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.dm.itest.api;
+//import static org.ops4j.pax.exam.CoreOptions.waitForFrameworkStartupFor;
+//import static org.ops4j.pax.exam.container.def.PaxRunnerOptions.vmOption;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX3057_EmptyServiceReferenceArray extends TestBase {
+    public void testWithoutIndex() throws Exception {
+        executeTest(context);
+    }
+    
+    public void testWithIndex() throws Exception {
+        System.setProperty(DependencyManager.SERVICEREGISTRY_CACHE_INDICES, "objectClass");
+        executeTest(context);
+    }
+
+    private void executeTest(BundleContext context) throws InvalidSyntaxException {
+        DependencyManager m = getDM();
+        Assert.assertNull("Looking up a non-existing service should return null.", m.getBundleContext().getServiceReferences(Service.class.getName(), "(objectClass=*)"));
+        Assert.assertNull("Looking up a non-existing service should return null.", m.getBundleContext().getAllServiceReferences(Service.class.getName(), "(objectClass=*)"));
+        Assert.assertNull("Looking up a non-existing service should return null.", m.getBundleContext().getServiceReference(Service.class.getName()));
+    }
+
+    /** Dummy interface for lookup. */
+    public static interface Service {}
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3337_UpdatedConfigurationDependencyWithPropagationTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3337_UpdatedConfigurationDependencyWithPropagationTest.java
new file mode 100644
index 0000000..368acb8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX3337_UpdatedConfigurationDependencyWithPropagationTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.dm.itest.api;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+
+/**
+ * This test validates the following scenario:
+ * - Service S1 depends on a ConfigurationDependency with propagate = true
+ * - Service S2 depends on S1 (and has access to the S1 configuration using the S1 service 
+ *   properties (because the ConfigurationDependency is propagated)
+ * - then the S1 PID is updated from ConfigAdmin
+ * - S1 is then called in its updated callback
+ * - S2 is called in its "change" callback.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class FELIX3337_UpdatedConfigurationDependencyWithPropagationTest extends TestBase {    
+    /*
+     * This Pojo creates the configuration pid "test".
+     */
+    static class ConfigurationCreator {
+        private volatile ConfigurationAdmin m_ca;
+        org.osgi.service.cm.Configuration m_conf;
+        
+        public void init() {
+            try {
+                m_conf = m_ca.getConfiguration("test", null);
+                Hashtable props = new Properties();
+                props.put("testkey", "testvalue");
+                m_conf.update(props);
+            }
+            catch (IOException e) {
+                Assert.fail("Could not create configuration: " + e.getMessage());
+            }
+        }
+        
+        public void update() {
+            try {
+                Hashtable props = new Properties();
+                props.put("testkey", "testvalue");
+                props.put("testkey2", "testvalue2");
+                m_conf.update(props);
+            } catch (IOException e) {
+                Assert.fail("Could not update the configured property: " + e.toString());
+            }
+        }
+    }
+
+    static class S1 implements ManagedService {
+        private Ensure m_ensure;
+        private boolean m_initialized;
+
+        public S1(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void updated(Dictionary props) throws ConfigurationException {
+        	if (props != null) {
+				if (!m_initialized) {
+					m_ensure.step(1);
+					m_initialized = true;
+				} else {
+					// we are updated
+					m_ensure.step(3);
+				}
+        	}
+        }
+    }
+
+    static class S2 {
+        private final Ensure m_ensure;
+
+        public S2(Ensure e) {
+            m_ensure = e;
+        }
+                        
+        public void add(S1 s1) {
+            m_ensure.step(2);
+        }
+        
+        public void change(S1 runnable) {
+            m_ensure.step(4);
+        }
+    }
+    
+    public void testComponentWithRequiredUpdatedConfigurationAndServicePropertyPropagation() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();        
+        ConfigurationCreator confCreator = new ConfigurationCreator();
+        Component s1 = m.createComponent()
+                .setImplementation(new S1(e))
+                .setInterface(S1.class.getName(), null)
+                .add(m.createConfigurationDependency()
+                     .setPid("test")
+                     .setPropagate(true));
+        Component s2 = m.createComponent()
+                .setImplementation(new S2(e))
+                .add(m.createServiceDependency()
+                     .setService(S1.class, ("(testkey=testvalue)"))
+                     .setRequired(true)
+                     .setCallbacks("add", "change", null));
+        Component s3 = m.createComponent()
+                .setImplementation(confCreator)
+                .add(m.createServiceDependency()
+                     .setService(ConfigurationAdmin.class)
+                     .setRequired(true));
+
+        m.add(s1);
+        m.add(s2);
+        m.add(s3);
+        e.waitForStep(2, 5000);
+        confCreator.update();
+        e.waitForStep(4, 5000);
+        m.remove(s1);
+        m.remove(s2);
+        m.remove(s3);
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX4158_DependencyDeclarationTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX4158_DependencyDeclarationTest.java
new file mode 100644
index 0000000..11edb49
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX4158_DependencyDeclarationTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.dm.itest.api;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDeclaration;
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Bundle;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX4158_DependencyDeclarationTest extends TestBase {
+    public void testServiceDependencyDeclaration() {
+        DependencyManager m = getDM();
+        Component c = m.createComponent()
+            .setImplementation(new Object())
+            .add(m.createServiceDependency().setService(LogService.class, "(foo=bar)"));
+        
+        ComponentDeclaration cd = c.getComponentDeclaration();
+        ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+        Assert.assertNotNull(cdds);
+        Assert.assertNotNull(cdds.length == 1);
+        Assert.assertEquals(cdds[0].getName(), "org.osgi.service.log.LogService (foo=bar)");
+        Assert.assertEquals(cdds[0].getSimpleName(), "org.osgi.service.log.LogService");
+        Assert.assertNotNull(cdds[0].getFilter());
+        Assert.assertEquals(cdds[0].getFilter(), "(foo=bar)");
+        m.clear();
+    }
+    
+    public void testConfigurationDependencyDeclaration() {
+        DependencyManager m = getDM();
+        Component c = m.createComponent()
+            .setImplementation(new Object())
+            .add(m.createConfigurationDependency().setPid("foo"));
+        
+        ComponentDeclaration cd = c.getComponentDeclaration();
+        ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+        Assert.assertNotNull(cdds);
+        Assert.assertNotNull(cdds.length == 1);
+        Assert.assertEquals(cdds[0].getName(), "foo");
+        Assert.assertEquals(cdds[0].getSimpleName(), "foo");
+        Assert.assertNull(cdds[0].getFilter());
+        m.clear();
+    }
+    
+    public void testResourceDependencyDeclaration() throws MalformedURLException {
+        DependencyManager m = getDM();
+        Component c = m.createComponent()
+            .setImplementation(new Object())
+            .add(m.createResourceDependency()
+                  .setResource(new URL("file://localhost/path/to/file1.txt")));
+        
+        ComponentDeclaration cd = c.getComponentDeclaration();
+        ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+        Assert.assertNotNull(cdds);
+        Assert.assertNotNull(cdds.length == 1);
+        Assert.assertEquals(cdds[0].getName(), "file://localhost/path/to/file1.txt");
+        Assert.assertNotNull(cdds[0].getSimpleName());
+        Assert.assertEquals(cdds[0].getSimpleName(), "file://localhost/path/to/file1.txt");
+        Assert.assertNull(cdds[0].getFilter());
+        m.clear();
+    }
+    
+    public void testResourceDependencyDeclarationWithFilter() {
+        DependencyManager m = getDM();
+        Component c = m.createComponent()
+            .setImplementation(new Object())
+            .add(m.createResourceDependency()
+                .setFilter("(&(path=/path/to/*.txt)(host=localhost))"));
+        
+        ComponentDeclaration cd = c.getComponentDeclaration();
+        ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+        Assert.assertNotNull(cdds);
+        Assert.assertNotNull(cdds.length == 1);
+        Assert.assertEquals(cdds[0].getName(), ("(&(path=/path/to/*.txt)(host=localhost))"));
+        Assert.assertNull(cdds[0].getSimpleName());
+        Assert.assertNotNull(cdds[0].getFilter());
+        Assert.assertEquals(cdds[0].getFilter(), "(&(path=/path/to/*.txt)(host=localhost))");
+        m.clear();
+    }
+    
+    public void testBundleDependencyDeclaration() throws MalformedURLException {
+        DependencyManager m = getDM();
+        Component c = m.createComponent()
+            .setImplementation(new Object())
+            .add(m.createBundleDependency());
+        
+        ComponentDeclaration cd = c.getComponentDeclaration();
+        ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+        Assert.assertNotNull(cdds);
+        Assert.assertNotNull(cdds.length == 1);
+        Assert.assertEquals(cdds[0].getName(), "active installed resolved");
+        Assert.assertNotNull(cdds[0].getSimpleName());
+        Assert.assertEquals(cdds[0].getSimpleName(), "active installed resolved");
+        Assert.assertNull(cdds[0].getFilter());
+        m.clear();
+    }
+
+    public void testBundleDependencyDeclarationWithMask() throws MalformedURLException {
+        DependencyManager m = getDM();
+        Component c = m.createComponent()
+            .setImplementation(new Object())
+            .add(m.createBundleDependency()
+                .setStateMask( Bundle.ACTIVE | Bundle.RESOLVED));
+        
+        ComponentDeclaration cd = c.getComponentDeclaration();
+        ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+        Assert.assertNotNull(cdds);
+        Assert.assertNotNull(cdds.length == 1);
+        Assert.assertEquals(cdds[0].getName(), "active resolved");
+        Assert.assertNotNull(cdds[0].getSimpleName());
+        Assert.assertEquals(cdds[0].getSimpleName(), "active resolved");
+        Assert.assertNull(cdds[0].getFilter());
+        m.clear();
+    }
+    
+    public void testBundleDependencyDeclarationWithFilter() throws MalformedURLException {
+        DependencyManager m = getDM();
+        Component c = m.createComponent()
+            .setImplementation(new Object())
+            .add(m.createBundleDependency()
+                .setStateMask( Bundle.ACTIVE )
+                .setFilter("(DependencyManager-Component=*)"));
+        
+        ComponentDeclaration cd = c.getComponentDeclaration();
+        ComponentDependencyDeclaration[] cdds = cd.getComponentDependencies();
+        Assert.assertNotNull(cdds);
+        Assert.assertNotNull(cdds.length == 1);
+        Assert.assertEquals(cdds[0].getName(), "active (DependencyManager-Component=*)");
+        Assert.assertNotNull(cdds[0].getSimpleName());
+        Assert.assertEquals(cdds[0].getSimpleName(), "active");
+        Assert.assertNotNull(cdds[0].getFilter());
+        Assert.assertEquals(cdds[0].getFilter(), "(DependencyManager-Component=*)");
+        m.clear();
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX4361_ConcurrentComponentListingTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX4361_ConcurrentComponentListingTest.java
new file mode 100644
index 0000000..0752c3f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX4361_ConcurrentComponentListingTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * Test for FELIX-4361 The DependencyManager.getComponents method failed in a concurrent situation on iterating the
+ * result of the method.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX4361_ConcurrentComponentListingTest extends TestBase {
+
+    @SuppressWarnings("rawtypes")
+    public void testConcurrentGetComponentsManipulation() {
+        DependencyManager dm = getDM();
+        dm.add(dm.createComponent().setImplementation(Object.class));
+        Iterator iterator = dm.getComponents().iterator();
+        dm.add(dm.createComponent().setImplementation(Object.class));
+        iterator.next();
+        dm.clear();
+    }
+
+    public void testConcurrentGetComponentsMultipleThreads() {
+        final DependencyManager m = getDM();
+        final AtomicInteger errors = new AtomicInteger(0);
+        final AtomicInteger componentsAdded = new AtomicInteger(0);
+        final int max = 10000;
+        final AtomicBoolean isRunning = new AtomicBoolean(true);
+
+        ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);
+        Runnable readTask = new Runnable() {
+            @SuppressWarnings({ "rawtypes", "unused" })
+            public void run() {
+                while (isRunning.get()) {
+                    try {
+                        List components = m.getComponents();
+                        for (Object component : components) {
+                            // Just iterating the components should check for concurrent modifications
+                        }
+                    }
+                    catch (Exception ex) {
+                        errors.addAndGet(1);
+                        ex.printStackTrace();
+                    }
+                }
+            }
+        };
+
+        Callable<Boolean> modifyTask = new Callable<Boolean>() {
+            public Boolean call() throws Exception {
+                try {
+                    m.add(m.createComponent().setImplementation(Object.class));
+                    componentsAdded.addAndGet(1);
+                    return true;
+                }
+                catch (Exception ex) {
+                    return false;
+                }
+            }
+        };
+
+        executorService.submit(readTask);
+        for (int i = 0; i < max; i++) {
+            executorService.submit(modifyTask);
+        }
+        isRunning.set(false);
+        executorService.shutdown();
+
+        try {
+            executorService.awaitTermination(30, TimeUnit.SECONDS);
+        }
+        catch (InterruptedException e) {
+        }
+        Assert.assertEquals(0, errors.get());
+        Assert.assertEquals(max, componentsAdded.get());
+        m.clear();
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX_4168AdapterWithDynamicallyAddedDependencies.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX_4168AdapterWithDynamicallyAddedDependencies.java
new file mode 100644
index 0000000..cffdcf9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX_4168AdapterWithDynamicallyAddedDependencies.java
@@ -0,0 +1,110 @@
+/**
+ * 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.dm.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX_4168AdapterWithDynamicallyAddedDependencies extends TestBase {
+    public void testAdapterWithExtraDependenciesAndCallbacks() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+
+        // create a service S2, which will be added to A1 (needs to be available)
+        Component s2 = m.createComponent().setInterface(S2.class.getName(), null).setImplementation(new S2Impl(e));
+        m.add(s2);
+
+        // create a service adapter that adapts to services S1 and has an optional dependency on services S2
+        Component sa = m.createAdapterService(S1.class, null).setImplementation(SA.class);
+        m.add(sa);
+
+        // create a service S1, which triggers the creation of the first adapter instance (A1)
+        Component s1 = m.createComponent().setInterface(S1.class.getName(), null).setImplementation(new S1Impl());
+        m.add(s1);
+
+        // create a second service S1, which triggers the creation of the second adapter instance (A2)
+        Component s1b = m.createComponent().setInterface(S1.class.getName(), null).setImplementation(new S1Impl());
+        m.add(s1b);
+
+        // observe that S2 is also added to A2
+        e.waitForStep(2, 5000);
+
+        // remove S2 again
+        m.remove(s2);
+
+        // make sure both adapters have their "remove" callbacks invoked
+        e.waitForStep(4, 5000);
+        m.clear();
+    }
+
+    static interface S1 {
+    }
+
+    static interface S2 {
+        public void invoke();
+    }
+
+    static class S1Impl implements S1 {
+    }
+
+    static class S2Impl implements S2 {
+
+        private final Ensure m_e;
+
+        public S2Impl(Ensure e) {
+            m_e = e;
+        }
+
+        public void invoke() {
+            m_e.step();
+        }
+    }
+
+    public static class SA {
+        volatile S2 s2;
+        volatile Component component;
+        volatile DependencyManager manager;
+
+        public SA() {
+            System.out.println("Adapter created");
+        }
+
+        public void init() {
+            System.out.println("Adapter init " + s2);
+            component.add(manager.createServiceDependency()
+                .setService(S2.class).setCallbacks("add", "remove").setRequired(true));
+        }
+
+        public void add(S2 s) {
+            System.out.println("adding " + s);
+            s.invoke();
+        }
+
+        public void remove(S2 s) {
+            System.out.println("removing " + s);
+            s.invoke();
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FactoryConfigurationAdapterTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FactoryConfigurationAdapterTest.java
new file mode 100644
index 0000000..315b3b8
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FactoryConfigurationAdapterTest.java
@@ -0,0 +1,224 @@
+/*
+ * 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.dm.itest.api;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class FactoryConfigurationAdapterTest extends TestBase
+{
+    private static Ensure m_ensure;
+    
+    public void testFactoryConfigurationAdapter() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        m_ensure = new Ensure();
+        
+        // Create a Configuration instance, which will create/update/remove a configuration for factoryPid "MyFactoryPid"
+        ConfigurationCreator configurator = new ConfigurationCreator("MyFactoryPid", "key", "value1");
+        Component s1 = m.createComponent()
+            .setImplementation(configurator)
+            .add(m.createServiceDependency()
+                .setService(ConfigurationAdmin.class)
+                .setRequired(true));
+
+        // Create an Adapter that will be instantiated, once the configuration is created.
+        // This Adapter provides an AdapterService, and depends on an AdapterExtraDependency service.
+        Component s2 = m.createFactoryConfigurationAdapterService("MyFactoryPid", "updated", true /* propagate CM settings */)
+                      .setInterface(AdapterService.class.getName(), new Hashtable() {{ put("foo", "bar"); }})
+                      .setImplementation(Adapter.class);
+
+        s2.add(m.createServiceDependency()
+            .setService(AdapterExtraDependency.class)
+            .setRequired(true)
+            .setAutoConfig(true));
+        
+        // Create extra adapter service dependency upon which our adapter depends on.
+        Component s3 = m.createComponent()
+            .setImplementation(new AdapterExtraDependency())
+            .setInterface(AdapterExtraDependency.class.getName(), null);
+        
+        // Create an AdapterService Consumer
+        Component s4 = m.createComponent()
+            .setImplementation(AdapterServiceConsumer.class)
+            .add(m.createServiceDependency()
+                .setService(AdapterService.class)
+                .setRequired(true)
+                .setCallbacks("bind", "change", "remove"));
+        
+        // Start services
+        m.add(s1);
+        m.add(s2);
+        m.add(s3);
+        m.add(s4);
+        
+        // Wait for step 8: the AdapterService consumer has been injected with the AdapterService, and has called the doService method.
+        m_ensure.waitForStep(8, 10000);
+        
+        // Modify configuration.
+        configurator.update("key", "value2");
+        
+        // Wait for step 13: the AdapterService has been updated, and the AdapterService consumer has seen the change
+        m_ensure.waitForStep(13, 10000);
+        
+        // Remove the configuration
+        m.remove(s1); // The stop method will remove the configuration
+        m_ensure.waitForStep(16, 10000);
+        m.clear();
+    }
+
+    public static class ConfigurationCreator {
+        private volatile ConfigurationAdmin m_ca;
+        private String m_key;
+        private String m_value;
+        private org.osgi.service.cm.Configuration m_conf;
+        private String m_factoryPid;
+        
+        public ConfigurationCreator(String factoryPid, String key, String value) {
+            m_factoryPid = factoryPid;
+            m_key = key;
+            m_value = value;
+        }
+
+        public void start() {
+            try {
+                m_ensure.step(1);
+                m_conf = m_ca.createFactoryConfiguration(m_factoryPid, null);
+                Hashtable props = new Hashtable();
+                props.put(m_key, m_value);
+                m_conf.update(props);
+            }
+            catch (IOException e) {
+                Assert.fail("Could not create configuration: " + e.getMessage());
+            }
+        }
+        
+        public void update(String key, String val) {
+            Hashtable props = new Hashtable();
+            props.put(key, val);
+            try {
+                m_conf.update(props);
+            }
+            catch (IOException e) {
+                Assert.fail("Could not update configuration: " + e.getMessage());
+            }
+        }
+        
+        public void stop() {
+            try
+            {
+                m_conf.delete();
+            }
+            catch (IOException e)
+            {
+                Assert.fail("Could not remove configuration: " + e.toString());
+            }
+        }
+    }
+    
+    public interface AdapterService {
+        public void doService();
+    }
+    
+    public static class AdapterExtraDependency {
+    }
+
+    public static class Adapter implements AdapterService {
+        volatile AdapterExtraDependency m_extraDependency; // extra dependency.
+        private int updateCount;
+        
+        void updated(Dictionary settings) {
+            updateCount ++;
+            if (updateCount == 1) {
+                m_ensure.step(2);
+                Assert.assertEquals(true, "value1".equals(settings.get("key")));
+                m_ensure.step(3);
+            } else if (updateCount == 2) {
+                m_ensure.step(9);
+                Assert.assertEquals(true, "value2".equals(settings.get("key")));
+                m_ensure.step(10);
+            } else {
+                Assert.fail("wrong call to updated method: count=" + updateCount);
+            }
+        }
+
+        public void doService() {   
+            m_ensure.step(8);
+        }
+        
+        public void start() {
+            m_ensure.step(4);
+            Assert.assertNotNull(m_extraDependency);
+            m_ensure.step(5);
+        }
+        
+        public void stop() {
+            m_ensure.step(16);
+        }
+    }
+
+    public static class AdapterServiceConsumer {
+        private AdapterService m_adapterService;
+        private Map m_adapterServiceProperties;
+        
+        void bind(Map serviceProperties, AdapterService adapterService) {
+            m_ensure.step(6);
+            m_adapterService = adapterService;
+            m_adapterServiceProperties = serviceProperties;
+        }
+        
+        void change(Map serviceProperties, AdapterService adapterService) {
+            m_ensure.step(11);
+            Assert.assertEquals(true, "value2".equals(m_adapterServiceProperties.get("key")));
+            m_ensure.step(12);
+            Assert.assertEquals(true, "bar".equals(m_adapterServiceProperties.get("foo")));
+            m_ensure.step(13);
+        }
+        
+        public void start() {
+            m_ensure.step(7);
+            Assert.assertNotNull(m_adapterService);
+            Assert.assertEquals(true, "value1".equals(m_adapterServiceProperties.get("key")));
+            Assert.assertEquals(true, "bar".equals(m_adapterServiceProperties.get("foo")));
+            m_adapterService.doService();
+        }
+        
+        public void stop() {
+            m_ensure.step(14);
+        }
+        
+        void remove(AdapterService adapterService) {
+            m_ensure.step(15);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FilterIndexResourceAdapterTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FilterIndexResourceAdapterTest.java
new file mode 100644
index 0000000..56437d9
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FilterIndexResourceAdapterTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.dm.itest.api;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.junit.Assert;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FilterIndexResourceAdapterTest extends TestBase {
+  public void testBasicResourceAdapter() throws Exception {
+      System.setProperty("org.apache.felix.dependencymanager.filterindex", "objectClass");
+      DependencyManager m = getDM();
+      // helper class that ensures certain steps get executed in sequence
+      Ensure e = new Ensure();
+      // create a resource provider
+      ResourceProvider provider = new ResourceProvider(context, new URL("file://localhost/path/to/file1.txt"));
+      // activate it
+      m.add(m.createComponent().setImplementation(provider).add(m.createServiceDependency().setService(ResourceHandler.class).setCallbacks("add", "remove")));
+      // create a resource adapter for our single resource
+      // note that we can provide an actual implementation instance here because there will be only one
+      // adapter, normally you'd want to specify a Class here
+      m.add(m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, null, "changed")
+            .setImplementation(new ResourceAdapter(e)));
+      // wait until the single resource is available
+      e.waitForStep(3, 5000);
+      // trigger a 'change' in our resource
+      provider.change();
+      // wait until the changed callback is invoked
+      e.waitForStep(4, 5000);
+      m.clear();
+   }
+  
+  static class ResourceAdapter {
+      protected URL m_resource; // injected by reflection.
+      private Ensure m_ensure;
+      
+      ResourceAdapter(Ensure e) {
+          m_ensure = e;
+      }
+      
+      public void start() {
+          m_ensure.step(1);
+          Assert.assertNotNull("resource not injected", m_resource);
+          m_ensure.step(2);
+          try {
+              @SuppressWarnings("unused")
+              InputStream in = m_resource.openStream();
+          } 
+          catch (FileNotFoundException e) {
+              m_ensure.step(3);
+          }
+          catch (IOException e) {
+              Assert.fail("We should not have gotten this exception.");
+          }
+      }
+      
+      public void changed() {
+          m_ensure.step(4);
+      }
+  }  
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/InstanceBoundDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/InstanceBoundDependencyTest.java
new file mode 100644
index 0000000..988588d
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/InstanceBoundDependencyTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This test does some injection tests on components being in INSTANTIATED_AND_WAITING_FOR_REQUIRED state.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class InstanceBoundDependencyTest extends TestBase {
+    Ensure m_e;
+    
+    public void testServiceInjection() {
+        DependencyManager m = getDM();
+        m_e = new Ensure();
+        
+        // Create a "C" component: it depends on some S1 services, and on some S2 instance-bound services (declared from C.init() method)        
+        C cimpl = new C();
+        Component c = m.createComponent().setImplementation(cimpl);
+        c.add(m.createServiceDependency().setService(S1.class).setRequired(true).setCallbacks("addS1", "changeS1", "removeS1").setAutoConfig(true));
+        m.add(c);
+        
+        // Add S1 (s1_1): C.add(S1 s1) is called, then init() is called where a dependency is declared on S2
+        Hashtable s1_1_props = new Hashtable();
+        s1_1_props.put("name", "s1_1");
+        s1_1_props.put(Constants.SERVICE_RANKING, new Integer(10));
+        S1Impl s1_1_impl = new S1Impl();
+        Component s1_1 = m.createComponent().setImplementation(s1_1_impl).setInterface(S1.class.getName(), s1_1_props);
+        m.add(s1_1);
+        m_e.waitForStep(1, 5000); // wait until C.init called
+        ServiceReference ref = cimpl.getS1("s1_1");
+        Assert.assertNotNull(ref);
+        Assert.assertNotNull(cimpl.getS1());
+        Assert.assertEquals(s1_1_impl, cimpl.getS1());
+        
+        // At this point, MyComponent is in INSTANTIATED_AND_WAITING_FOR_REQUIRED state. 
+        // add now add another higher ranked S1 (s1_2) instance. C.add(s1_2) method should be called (the S1 dependency 
+        // is not instance bound), and m_s1 autoconfig field should be updated.
+        Hashtable s1_2_props = new Hashtable();
+        s1_2_props.put(Constants.SERVICE_RANKING, new Integer(20));
+        s1_2_props.put("name", "s1_2");
+        S1Impl s1_2_impl = new S1Impl();
+        Component s1_2 = m.createComponent().setImplementation(s1_2_impl).setInterface(S1.class.getName(), s1_2_props);
+        m.add(s1_2);
+        ref = cimpl.getS1("s1_2");
+        Assert.assertNotNull(ref);
+        Assert.assertNotNull(cimpl.getS1()); 
+        Assert.assertEquals(s1_2_impl, cimpl.getS1()); // must return s1_2 with ranking = 20
+
+        // Now, change the s1_1 service properties: C.changed(s1_1) should be called, and C.m_s1AutoConfig should be updated
+        s1_1_props.put(Constants.SERVICE_RANKING, new Integer(30));
+        s1_1.setServiceProperties(s1_1_props);
+        ref = cimpl.getS1("s1_1");
+        Assert.assertNotNull(ref);
+        Assert.assertEquals(new Integer(30), ref.getProperty(Constants.SERVICE_RANKING));
+        Assert.assertNotNull(cimpl.getS1());
+        Assert.assertEquals(s1_1_impl, cimpl.getS1());
+        
+        // Now, remove the s1_1: C.remove(s1_1) should be called, and C.m_s1AutoConfig should be updated
+        m.remove(s1_1);
+        ref = cimpl.getS1("s1_1");
+        Assert.assertNull(cimpl.getS1("s1_1"));
+        Assert.assertNotNull(cimpl.getS1());
+        Assert.assertEquals(s1_2_impl, cimpl.getS1());
+        m.clear();
+    }
+    
+    // C component depends on some S1 required services
+    public interface S1 {
+    }
+    
+    public class S1Impl implements S1 {
+    }
+    
+    public interface S2 {        
+    }
+    
+    public class S2Impl implements S2 {        
+    }
+    
+    // Our "C" component: it depends on S1 (required) and S2 (required/instance bound)
+    class C {        
+        final Map<String, ServiceReference> m_s1Map = new HashMap();
+        final Map<String, ServiceReference> m_s2Map = new HashMap();
+        volatile S1 m_s1; // auto configured
+        
+        S1 getS1() {
+            return m_s1;
+        }
+
+        void addS1(ServiceReference s1) {
+            m_s1Map.put((String) s1.getProperty("name"), s1);
+        }
+        
+        void changeS1(ServiceReference s1) {
+            m_s1Map.put((String) s1.getProperty("name"), s1);
+        }
+        
+        void removeS1(ServiceReference s1) {
+            m_s1Map.remove((String) s1.getProperty("name"));
+        }
+        
+        void addS2(ServiceReference s2) {
+            m_s2Map.put((String) s2.getProperty("name"), s2);
+        }
+        
+        void changeS2(ServiceReference s2) {
+            m_s2Map.put((String) s2.getProperty("name"), s2);
+        }
+        
+        void removeS2(ServiceReference s2) {
+            m_s2Map.remove((String) s2.getProperty("name"));
+        }
+        
+        ServiceReference getS1(String name) {
+            return m_s1Map.get(name);
+        }
+        
+        ServiceReference getS2(String name) {
+            return m_s2Map.get(name);
+        }
+        
+        void init(Component c) {
+            DependencyManager m = c.getDependencyManager();
+            c.add(m.createServiceDependency().setService(S2.class).setRequired(true).setCallbacks("addS2", "changeS2", "removeS2"));
+            m_e.step(1);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ModifiedBundleDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ModifiedBundleDependencyTest.java
new file mode 100644
index 0000000..76d681c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ModifiedBundleDependencyTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.dm.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+/**
+ * Test for FELIX-4334 issue.
+ * 
+ * Two components: A, B
+ * 
+ * - A provided.
+ * - B has a bundle dependency on the dependency manager shell bundle, which is currently stopped.
+ * - B has an instance bound dependency on A.
+ * - Now unregister A.
+ * - As a result of that, B becomes unavailable and is unbound from A. But B is not destroyed, because A dependency 
+ *   is "instance bound". So B is still bound to the bundle dependency.
+ * - Now, someone starts the dependency manager shell bundle: B then shall be called in its "changed" callback.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ModifiedBundleDependencyTest extends TestBase {
+    public static interface A {
+    }
+    
+    static class AImpl implements A {
+    }
+        
+    public static interface B {
+    }
+    
+    static class BImpl implements B {
+        final Ensure m_e;
+        
+        BImpl(Ensure e) {
+            m_e = e;
+        }
+        
+        public void add(Bundle dmTest) {
+            m_e.step(1);
+        }
+
+        void init(Component c) {
+            m_e.step(2);
+            DependencyManager dm = c.getDependencyManager();
+            c.add(dm.createServiceDependency().setService(A.class).setRequired(true).setCallbacks("add", "remove"));
+        }      
+        
+        public void add(A a) {
+            m_e.step(3);
+        }
+        
+        public void start() {
+            m_e.step(4);            
+        }
+        
+        public void stop() {
+            m_e.step(5);
+        }
+
+        public void remove(A a) {
+            m_e.step(6);
+        }
+
+        public void change(Bundle dmTest) { // called two times: one for STARTING, one for STARTED
+            m_e.step(); 
+        }
+        
+        public void destroy() {
+            m_e.step(9);
+        }
+
+        public void remove(Bundle dmTest) {   
+            m_e.step(10);
+        }                    
+    }
+    
+    public void testAdapterWithChangedInstanceBoundDependency() {
+        DependencyManager m = getDM();
+        Ensure e = new Ensure();
+
+        Component a = m.createComponent()
+                .setImplementation(new AImpl())
+                .setInterface(A.class.getName(), null);
+        
+        Component b = m.createComponent()
+                .setInterface(B.class.getName(), null)
+                .setImplementation(new BImpl(e))
+                .add(m.createBundleDependency()
+                        .setFilter("(Bundle-SymbolicName=org.apache.felix.metatype)")
+                        .setStateMask(Bundle.INSTALLED|Bundle.ACTIVE|Bundle.RESOLVED|Bundle.STARTING)
+                        .setRequired(true)
+                        .setCallbacks("add", "change", "remove"));
+                    
+        Bundle dmtest = getBundle("org.apache.felix.metatype");
+        try {
+            dmtest.stop();
+        } catch (BundleException e1) {
+            Assert.fail("could not find metatype bundle");
+        }
+        
+        m.add(a);
+        m.add(b);
+        
+        e.waitForStep(4, 5000);        
+        m.remove(a); // B will loose A and will enter into "waiting for required (instantiated)" state.
+        System.out.println("Starting metatype bundle ...");        
+        try {
+            dmtest.start();
+        } catch (BundleException e1) {
+            Assert.fail("could not start metatype bundle");
+        }
+        e.waitForStep(7, 5000);     
+        m.remove(b);        
+        e.waitForStep(10, 5000);                
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependenciesTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependenciesTest.java
new file mode 100644
index 0000000..32f7855
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependenciesTest.java
@@ -0,0 +1,222 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Hashtable;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class MultipleExtraDependenciesTest extends TestBase {
+    /**
+     * Check that list of extra dependencies (defined from init method) are handled properly.
+     * The extra dependencies are added using a List object (Component.add(List)).
+     * A component c1 will define two extra dependencies over *available* c4/c5 services.
+     */
+     public void testWithTwoAvailableExtraDependency() {   
+         DependencyManager m = getDM();
+         // Helper class that ensures certain steps get executed in sequence
+         Ensure e = new Ensure();
+         Component c1 = m.createComponent()
+                         .setInterface(Service1.class.getName(), null)
+                         .setImplementation(new MyComponent1(e))
+                         .add(m.createServiceDependency()
+                              .setService(Service2.class)
+                              .setRequired(true)
+                              .setAutoConfig("m_service2"));
+         
+         Component c2 = m.createComponent()
+                         .setImplementation(new MyComponent2(e))
+                         .add(m.createServiceDependency()
+                              .setService(Service1.class)
+                              .setRequired(false)
+                              .setAutoConfig(false)
+                              .setCallbacks("added", null, null));
+              
+         Component c3 = m.createComponent()
+                         .setInterface(Service2.class.getName(), null)
+                         .setImplementation(Service2Impl.class);
+         
+         Hashtable h = new Hashtable();
+         h.put("type", "xx");
+         Component c4 = m.createComponent()
+                         .setInterface(Service3.class.getName(), h)
+                         .setImplementation(Service3Impl1.class);
+
+         h = new Hashtable();
+         h.put("type", "yy");
+         Component c5 = m.createComponent()
+                         .setInterface(Service3.class.getName(), h)
+                         .setImplementation(Service3Impl2.class);
+
+
+         System.out.println("\n+++ Adding c2 / MyComponent2");
+         m.add(c2);
+         System.out.println("\n+++ Adding c3 / Service2");
+         m.add(c3);
+         System.out.println("\n+++ Adding c4 / Service3(xx)");
+         m.add(c4);
+         System.out.println("\n+++ Adding c5 / Service3(yy)");
+         m.add(c5);
+         System.out.println("\n+++ Adding c1 / MyComponent1");
+         // c1 have declared two extra dependency on Service3 (xx/yy).
+         // both extra dependencies are available, so the c1 component should be started immediately.
+         m.add(c1);
+         e.waitForStep(3, 3000);
+         m.clear();
+     }
+
+    /**
+     * Check that list of extra dependencies (defined from init method) are handled properly.
+     * The extra dependencies are added using a List object (Component.add(List)).
+     * A component c1 will define two extra dependencies over c4/c5. At the point c1.init()
+     * is adding the two extra dependencies from its init method, c4 is available, but not c5.
+     * So, c1 is not yet activated.
+     * Then c5 is added, and it triggers the c1 activation ...
+     */
+    public void testWithOneAvailableExtraDependency() {  
+        DependencyManager m = getDM();
+        // Helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        Component c1 = m.createComponent()
+                        .setInterface(Service1.class.getName(), null)
+                        .setImplementation(new MyComponent1(e))
+                        .add(m.createServiceDependency()
+                             .setService(Service2.class)
+                             .setRequired(true)
+                             .setAutoConfig("m_service2"));
+        
+        Component c2 = m.createComponent()
+                        .setImplementation(new MyComponent2(e))
+                        .add(m.createServiceDependency()
+                             .setService(Service1.class)
+                             .setRequired(false)
+                             .setAutoConfig(false)
+                             .setCallbacks("added", null, null));
+             
+        Component c3 = m.createComponent()
+                        .setInterface(Service2.class.getName(), null)
+                        .setImplementation(Service2Impl.class);
+        
+        Hashtable h = new Hashtable();
+        h.put("type", "xx");
+        Component c4 = m.createComponent()
+                        .setInterface(Service3.class.getName(), h)
+                        .setImplementation(Service3Impl1.class);
+
+        h = new Hashtable();
+        h.put("type", "yy");
+        Component c5 = m.createComponent()
+                        .setInterface(Service3.class.getName(), h)
+                        .setImplementation(Service3Impl2.class);
+
+
+        System.out.println("\n+++ Adding c2 / MyComponent2");
+        m.add(c2);
+        System.out.println("\n+++ Adding c3 / Service2");
+        m.add(c3);
+        System.out.println("\n+++ Adding c4 / Service3(xx)");
+        m.add(c4);
+        System.out.println("\n+++ Adding c1 / MyComponent1");
+        m.add(c1);
+
+        // c1 have declared two extra dependency on Service3 (xx/yy).
+        // So, because we have not yet added c5 (yy), c1 should not be started currently.
+        // But, now, we'll add c5 (Service3/yy) and c1 should then be started ...
+        System.out.println("\n+++ Adding c5 / Service3(yy)");
+        m.add(c5);
+        e.waitForStep(3, 3000);
+        m.clear();
+    }
+
+
+    public interface Service1 {}
+    public interface Service2 {}
+    public interface Service3 {}
+
+    public static class Service2Impl implements Service2 {}
+    public static class Service3Impl1 implements Service3 {}
+    public static class Service3Impl2 implements Service3 {}
+
+    public static class MyComponent1 implements Service1 {
+        Service2 m_service2;
+        Service3 m_service3_xx;
+        Service3 m_service3_yy;
+        Ensure m_ensure;
+        
+        public MyComponent1(Ensure e) {
+            m_ensure = e;
+        }
+
+        void init(Component c) {
+            m_ensure.step(1);
+            DependencyManager dm = c.getDependencyManager();
+            // Service3/xx currently available
+            Dependency d1 = 
+            		dm.createServiceDependency()
+                     .setService(Service3.class, "(type=xx)")
+                     .setRequired(true)
+                     .setAutoConfig("m_service3_xx");
+            
+            // Service3/yy not yet available
+            Dependency d2 = 
+            	dm.createServiceDependency()
+                  .setService(Service3.class, "(type=yy)")
+                  .setRequired(true)
+                  .setAutoConfig("m_service3_yy");
+            c.add(d1, d2);
+        }
+        
+        void start() {
+            System.out.println("MyComponent1.start");
+            Assert.assertNotNull(m_service2);
+            Assert.assertNotNull(m_service3_xx);
+            Assert.assertNotNull(m_service3_yy);
+            m_ensure.step(2);
+        }
+    }
+    
+    public static class MyComponent2 {
+        Ensure m_ensure;
+        
+        public MyComponent2(Ensure e) {
+            m_ensure = e;
+        }
+
+        void added(Service1 s1) {
+            System.out.println("MyComponent2.bind(" + s1 + ")");
+            Assert.assertNotNull(s1);
+            m_ensure.step(3);
+        }
+        
+        void start() {
+            System.out.println("MyComponent2.start");
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependencyTest.java
new file mode 100644
index 0000000..9816b0f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependencyTest.java
@@ -0,0 +1,226 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Hashtable;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * Test which validates multi-dependencies combination.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class MultipleExtraDependencyTest extends TestBase {
+    public void testMultipleExtraDependencies()
+    {
+        DependencyManager m = getDM();
+        Ensure e = new Ensure();
+        
+        Component sp2 = m.createComponent()
+              .setImplementation(ServiceProvider2.class).setInterface(ServiceProvider2.class.getName(), null)
+              .add(m.createServiceDependency()
+                   .setService(Runnable.class, "(foo=bar)")
+                   .setRequired(false)
+                   .setAutoConfig("m_runnable"))
+              .add(m.createServiceDependency()
+                   .setService(Sequencer.class)
+                   .setRequired(true)
+                   .setCallbacks("bind", null))
+              .setCallbacks(null, "start", "stop", null)
+              .setComposition("getComposition");
+        
+        Component sp = m.createComponent()
+              .setImplementation(ServiceProvider.class)
+              .setInterface(ServiceInterface.class.getName(), 
+                            new Hashtable() {{ put("foo", "bar"); }})
+              .add(m.createServiceDependency()
+                   .setService(Sequencer.class)
+                   .setRequired(true)
+                   .setAutoConfig("m_sequencer"))
+               .add(m.createServiceDependency()
+                   .setService(ServiceProvider2.class)
+                   .setRequired(true)
+                   .setCallbacks("bind", "unbind"))
+              .setCallbacks(null, "start", "stop", null);
+        
+        Component sc = m.createComponent()
+              .setImplementation(ServiceConsumer.class)
+              .add(m.createServiceDependency()
+                   .setService(Sequencer.class)
+                   .setRequired(true)
+                   .setAutoConfig("m_sequencer"))
+               .add(m.createServiceDependency()
+                   .setService(ServiceInterface.class, "(foo=bar)")
+                   .setRequired(true)
+                   .setAutoConfig("m_service"))
+              .setCallbacks(null, "start", "stop", null);
+        
+        Component sequencer = 
+            m.createComponent().setImplementation(new SequencerImpl(e))
+                             .setInterface(Sequencer.class.getName(), null);
+        m.add(sp2);
+        m.add(sp);
+        m.add(sc);
+        m.add(sequencer);
+        
+        // Check if ServiceProvider component have been initialized orderly
+        e.waitForStep(7, 5000);
+        
+        // Stop the test.annotation bundle
+        m.remove(sequencer);
+        m.remove(sp);
+        m.remove(sp2);
+        m.remove(sc);
+        
+        // And check if ServiceProvider2 has been deactivated orderly
+        e.waitForStep(11, 5000);
+    }
+    
+    public interface Sequencer
+    {
+        void step();
+        void step(int step);
+        void waitForStep(int step, int timeout);
+    }
+    
+    public static class SequencerImpl implements Sequencer {
+        Ensure m_ensure;
+        
+        public SequencerImpl(Ensure e)
+        {
+            m_ensure = e;
+        }
+        
+        public void step()
+        {
+            m_ensure.step();
+        }
+
+        public void step(int step)
+        {
+            m_ensure.step(step);
+        }
+
+        public void waitForStep(int step, int timeout)
+        {
+            m_ensure.waitForStep(step, timeout);
+        }  
+    }
+    
+    public interface ServiceInterface
+    {
+        public void doService();
+    }
+    
+    public static class ServiceConsumer
+    {
+        volatile Sequencer m_sequencer;
+        volatile ServiceInterface m_service;
+
+        void start()
+        {
+            m_sequencer.step(6);
+            m_service.doService();
+        }
+
+        void stop()
+        {
+            m_sequencer.step(8);
+        }
+    }
+    
+    public static class ServiceProvider implements ServiceInterface
+    {
+        Sequencer m_sequencer;
+        ServiceProvider2 m_serviceProvider2;
+
+        void bind(ServiceProvider2 provider2)
+        {
+            m_serviceProvider2 = provider2;
+        }
+
+        void start()
+        {
+            m_serviceProvider2.step(4);
+            m_sequencer.step(5);
+        }
+
+        void stop()
+        {
+            m_sequencer.step(9);
+        }
+
+        void unbind(ServiceProvider2 provider2)
+        {
+            m_sequencer.step(10);
+        }
+
+        public void doService()
+        {
+            m_sequencer.step(7);
+        }
+    }
+
+    public static class ServiceProvider2
+    {
+        Composite m_composite = new Composite();
+        Sequencer m_sequencer;
+        Runnable m_runnable;
+
+        void bind(Sequencer seq)
+        {
+            m_sequencer = seq;
+            m_sequencer.step(1);
+        }
+
+        void start()
+        {
+            m_sequencer.step(3);
+            m_runnable.run(); // NullObject
+        }
+
+        public void step(int step) // called by ServiceProvider.start() method 
+        { 
+            m_sequencer.step(step);
+        }
+        
+        void stop()
+        {
+            m_sequencer.step(11);
+        }
+
+        Object[] getComposition()
+        {
+            return new Object[] { this, m_composite };
+        }
+    }
+    
+    public static class Composite
+    {
+        void bind(Sequencer seq)
+        {
+            seq.step(2);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependencyTest2.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependencyTest2.java
new file mode 100644
index 0000000..9889f3a
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleExtraDependencyTest2.java
@@ -0,0 +1,257 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Hashtable;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * Tests for extra dependencies which are declared from service's init method.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class MultipleExtraDependencyTest2 extends TestBase {
+    public void testMultipleExtraDependencies()
+    {
+        DependencyManager m = getDM();
+        Ensure e = new Ensure();
+        
+        Component sp2 = m.createComponent()
+            .setImplementation(ServiceProvider2.class).setInterface(ServiceProvider2.class.getName(), null)
+            .setCallbacks("init", "start", "stop", null)
+            .setComposition("getComposition");
+        
+        Component sp = m.createComponent()
+              .setImplementation(ServiceProvider.class)
+              .setInterface(ServiceInterface.class.getName(), new Hashtable() {{ put("foo", "bar"); }})                            
+              .setCallbacks("init", "start", "stop", null);
+        
+        Component sc = m.createComponent()
+              .setImplementation(ServiceConsumer.class)
+              .setCallbacks("init", "start", "stop", null);
+        
+        // Provide the Sequencer service to the MultipleAnnotationsTest class.
+        Component sequencer = 
+            m.createComponent().setImplementation(new SequencerImpl(e))
+                             .setInterface(Sequencer.class.getName(), null);
+        m.add(sp2);
+        m.add(sp);
+        m.add(sc);
+        m.add(sequencer);
+
+        // Check if the test.annotation components have been initialized orderly
+        e.waitForStep(7, 10000);
+        
+        // Stop the test.annotation bundle
+        m.remove(sequencer);
+        m.remove(sp);
+        m.remove(sp2);
+        m.remove(sc);
+        
+//        m.remove(sp2);
+//        m.remove(sc);
+//        m.remove(sp);
+//        m.remove(sequencer);
+        
+
+        
+        // And check if the test.annotation bundle has been deactivated orderly
+        e.waitForStep(11, 10000);
+        m.clear();
+    }
+    
+    public interface Sequencer
+    {
+        void step();
+        void step(int step);
+        void waitForStep(int step, int timeout);
+    }
+    
+    public static class SequencerImpl implements Sequencer {
+        final Ensure m_ensure;
+        
+        public SequencerImpl(Ensure e)
+        {
+            m_ensure = e;
+        }
+        
+        public void step()
+        {
+            m_ensure.step();
+        }
+
+        public void step(int step)
+        {
+            m_ensure.step(step);
+        }
+
+        public void waitForStep(int step, int timeout)
+        {
+            m_ensure.waitForStep(step, timeout);
+        }  
+    }
+    
+    public interface ServiceInterface
+    {
+        public void doService();
+    }
+    
+    public static class ServiceConsumer {
+        volatile Sequencer m_sequencer;
+        volatile ServiceInterface m_service;
+        volatile Dependency m_d1, m_d2;
+
+        public void init(Component s) {
+            DependencyManager m = s.getDependencyManager();
+            m_d1 = m.createServiceDependency()
+                .setService(Sequencer.class)
+                .setRequired(true)
+                .setAutoConfig("m_sequencer");
+            m_d2 = m.createServiceDependency()
+                .setService(ServiceInterface.class, "(foo=bar)")
+                .setRequired(true)
+                .setAutoConfig("m_service");
+            s.add(m_d1, m_d2);
+        }
+        
+        void start() {
+            m_sequencer.step(6);
+            m_service.doService();
+        }
+
+        void stop() {
+            m_sequencer.step(8);
+        }
+    }
+    
+    public static class ServiceProvider implements ServiceInterface
+    {
+        volatile Sequencer m_sequencer;
+        volatile ServiceProvider2 m_serviceProvider2;
+        volatile ServiceDependency m_d1, m_d2;
+
+        public void init(Component c)
+        {
+            DependencyManager m = c.getDependencyManager();
+            m_d1 = m.createServiceDependency()
+                    .setService(Sequencer.class)
+                    .setRequired(true)
+                    .setAutoConfig("m_sequencer");
+            m_d2 = m.createServiceDependency()
+                    .setService(ServiceProvider2.class)
+                    .setRequired(true)
+                    .setCallbacks("bind", "unbind");
+            c.add(m_d1, m_d2);
+        }
+        
+        void bind(ServiceProvider2 provider2)
+        {
+            m_serviceProvider2 = provider2;
+        }
+
+        void start()
+        {
+            m_serviceProvider2.step(4);
+            m_sequencer.step(5);
+        }
+
+        void stop()
+        {
+            m_sequencer.step(9);
+        }
+
+        void unbind(ServiceProvider2 provider2)
+        {
+            m_sequencer.step(10);
+        }
+
+        public void doService()
+        {
+            m_sequencer.step(7);
+        }
+    }
+
+    public static class ServiceProvider2
+    {
+        final Composite m_composite = new Composite();
+        volatile Sequencer m_sequencer;
+        volatile Runnable m_runnable;
+        volatile ServiceDependency m_d1, m_d2;
+
+        public void init(Component c)
+        {
+            System.out.println("ServiceProvider2.init");
+            DependencyManager m = c.getDependencyManager();
+            
+            m_d1 = m.createServiceDependency()
+                  .setService(Runnable.class, "(foo=bar)")
+                  .setRequired(false)
+                  .setAutoConfig("m_runnable");
+            m_d2 = m.createServiceDependency()
+                  .setService(Sequencer.class)
+                  .setRequired(true)
+                  .setCallbacks("bind", null);
+            c.add(m_d1, m_d2);
+        }
+        
+        void bind(Sequencer seq)
+        {
+            System.out.println("ServiceProvider2.bind(" + seq + ")");
+            m_sequencer = seq;
+            m_sequencer.step(1);
+        }
+
+        void start()
+        {
+            System.out.println("ServiceProvider2.start: m_runnable=" + m_runnable + ", m_sequencer = " + m_sequencer);
+            m_sequencer.step(3);
+            m_runnable.run(); // NullObject
+        }
+
+        public void step(int step) // called by ServiceProvider.start() method 
+        { 
+            m_sequencer.step(step);
+        }
+        
+        void stop()
+        {
+            m_sequencer.step(11);
+        }
+
+        Object[] getComposition()
+        {
+            return new Object[] { this, m_composite };
+        }
+    }
+    
+    public static class Composite
+    {
+        void bind(Sequencer seq)
+        {
+            seq.step(2);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleServiceDependencyTest.java
new file mode 100644
index 0000000..36874ee
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/MultipleServiceDependencyTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Hashtable;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.Constants;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class MultipleServiceDependencyTest extends TestBase {
+   public void testMultipleServiceRegistrationAndConsumption() {
+       DependencyManager m = getDM();
+       // helper class that ensures certain steps get executed in sequence
+       Ensure e = new Ensure();
+       // create a service provider and consumer
+       Component provider = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+       Component providerWithHighRank = m.createComponent().setImplementation(new ServiceProvider2(e)).setInterface(ServiceInterface.class.getName(), new Hashtable() {{ put(Constants.SERVICE_RANKING, Integer.valueOf(5)); }});
+       Component consumer = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+       m.add(provider);
+       m.add(providerWithHighRank);
+       m.add(consumer);
+       e.waitForStep(3, 5000);
+       m.remove(providerWithHighRank);
+       e.step(4);
+       e.waitForStep(5, 5000);
+       m.remove(provider);
+       m.remove(consumer);
+       e.waitForStep(6, 5000);
+   }
+
+   public void testReplacementAutoConfig() {
+       DependencyManager m = getDM();
+       // helper class that ensures certain steps get executed in sequence
+       Ensure e = new Ensure();
+       // create a service provider and consumer
+       Component provider = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+       Component provider2 = m.createComponent().setImplementation(new ServiceProvider2(e)).setInterface(ServiceInterface.class.getName(), null);
+       Component consumer = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+       m.add(provider2);
+       m.add(consumer);
+       e.waitForStep(3, 5000);
+       m.add(provider);
+       m.remove(provider2);
+       e.step(4);
+       e.waitForStep(5, 5000);
+       m.remove(provider);
+       m.remove(consumer);
+       e.waitForStep(6, 5000);
+   }
+
+   public void testReplacementCallbacks() {
+       DependencyManager m = getDM();
+       // helper class that ensures certain steps get executed in sequence
+       Ensure e = new Ensure();
+       // create a service provider and consumer
+       Component provider = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+       Component provider2 = m.createComponent().setImplementation(new ServiceProvider2(e)).setInterface(ServiceInterface.class.getName(), null);
+       Component consumer = m.createComponent().setImplementation(new ServiceConsumer(e))
+         .add(m.createServiceDependency()
+              .setService(ServiceInterface.class)
+              .setRequired(true)
+              .setCallbacks("add", "remove"));
+       m.add(provider2);
+       m.add(consumer);
+       e.waitForStep(3, 15000);
+       m.add(provider);
+       m.remove(provider2);
+       e.step(4);
+       e.waitForStep(5, 15000);
+       m.remove(provider);
+       m.remove(consumer);
+       e.waitForStep(6, 15000);
+   }
+
+   static interface ServiceInterface {
+       public void invoke();
+   }
+
+   static class ServiceProvider implements ServiceInterface {
+       private final Ensure m_ensure;
+       public ServiceProvider(Ensure e) {
+           m_ensure = e;
+       }
+       public void invoke() {
+           m_ensure.step(5);
+       }
+   }
+
+   static class ServiceProvider2 implements ServiceInterface {
+       private final Ensure m_ensure;
+       public ServiceProvider2(Ensure e) {
+           m_ensure = e;
+       }
+       public void invoke() {
+           m_ensure.step(2);
+       }
+   }
+
+   static class ServiceConsumer implements Runnable {
+       private volatile ServiceInterface m_service;
+       private final Ensure m_ensure;
+
+       @SuppressWarnings("unused")
+       private void add(ServiceInterface service) { m_service = service; }
+       
+       @SuppressWarnings("unused")
+       private void remove(ServiceInterface service) { if (m_service == service) { m_service = null; }}
+       public ServiceConsumer(Ensure e) { m_ensure = e; }
+
+       public void start() {
+           Thread t = new Thread(this);
+           t.start();
+       }
+
+       public void run() {
+           m_ensure.step(1);
+           m_service.invoke();
+           m_ensure.step(3);
+           m_ensure.waitForStep(4, 15000);
+           m_service.invoke();
+       }
+
+       public void stop() {
+           m_ensure.step(6);
+       }
+   }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/RemovedDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/RemovedDependencyTest.java
new file mode 100644
index 0000000..5d1af03
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/RemovedDependencyTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Properties;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * One consumer, Three providers. The Consumer has two required dependency on provider1, provider2, and one 
+ * instance-bound required dependency on provider3.
+ * When the three providers are there, the consumer is started.
+ * 
+ * This test asserts the following correct behaviors:
+ *   - when we remove the dependency on provider2, then the consumer is not stopped.
+ *   - when we remove the (instance-bound) dependency on provider3, then the consumer os not stopped.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "unused"})
+public class RemovedDependencyTest extends TestBase {
+    public void testRemoveDependencyAndConsumerMustRemainStarted() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // Create two providers
+        Hashtable props = new Hashtable();
+        props.put("name", "provider1");
+        Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), props);
+        props = new Properties();
+        props.put("name", "provider2");
+        Component sp2 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), props);
+        props = new Properties();
+        props.put("name", "provider3");
+        Component sp3 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), props);
+
+        // Create the consumer, and start it
+        Dependency d1 = m.createServiceDependency().setService(ServiceInterface.class, "(name=provider1)").setRequired(true).setCallbacks("add", "remove");
+        Dependency d2 = m.createServiceDependency().setService(ServiceInterface.class, "(name=provider2)").setRequired(true).setCallbacks("add", "remove");
+        Dependency d3 = m.createServiceDependency().setService(ServiceInterface.class, "(name=provider3)").setRequired(true).setCallbacks("add", "remove");
+
+        ServiceConsumer consumer = new ServiceConsumer(e, d3);
+        Component sc = m.createComponent().setImplementation(consumer).add(d1, d2);
+            
+        // Add the first two providers and the consumer
+        m.add(sp);
+        m.add(sp2);
+        m.add(sp3);
+        m.add(sc);
+        
+        // Check if consumer has been bound to the three providers
+        e.waitForStep(3,  5000);
+        Assert.assertEquals(3, consumer.getProvidersCount());
+        Assert.assertNotNull(consumer.getProvider("provider1"));
+        Assert.assertNotNull(consumer.getProvider("provider2"));
+        Assert.assertNotNull(consumer.getProvider("provider3"));
+        
+        // Now remove the provider2, and check if the consumer is still alive
+        sc.remove(d2);
+        Assert.assertFalse(consumer.isStopped());
+        Assert.assertEquals(2, consumer.getProvidersCount());
+        Assert.assertNotNull(consumer.getProvider("provider1"));
+        Assert.assertNull(consumer.getProvider("provider2"));
+        Assert.assertNotNull(consumer.getProvider("provider3"));
+
+        // Now remove the provider3 (the consumer has an instance bound dependency on it), and check if the consumer is still alive
+        sc.remove(d3);
+        Assert.assertFalse(consumer.isStopped());
+        Assert.assertEquals(1, consumer.getProvidersCount());
+        Assert.assertNotNull(consumer.getProvider("provider1"));
+        Assert.assertNull(consumer.getProvider("provider2"));
+        Assert.assertNull(consumer.getProvider("provider3"));
+        
+        m.clear();
+    }
+    
+    static interface ServiceInterface {
+        public void invoke();
+    }
+
+    class ServiceProvider implements ServiceInterface {
+        final Ensure m_ensure;
+        
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke() {
+            m_ensure.step();
+        }
+    }
+    
+    class ServiceConsumer {
+        private final Ensure m_ensure;
+        private final List<ServiceReference> m_providers = new ArrayList<>();
+        private BundleContext m_bc;
+        private boolean m_stopped;
+        private final Dependency m_dependency3;
+
+        public ServiceConsumer(Ensure e, Dependency dependency3) {
+            m_ensure = e;
+            m_dependency3 = dependency3;
+        }
+                
+        public void add(ServiceReference ref) {
+            debug("ServiceConsumer.add(%s)", ref);
+            m_providers.add(ref);
+            ServiceInterface s = (ServiceInterface) m_bc.getService(ref);
+            s.invoke();
+        }
+        
+        public void remove(ServiceReference ref) {
+            debug("ServiceConsumer.remove(%s)", ref);
+            m_providers.remove(ref);
+            debug("ServiceConsumer: current providers list=%s", m_providers);
+        }
+        
+        public void init(Component c) {
+            c.add(m_dependency3);
+        }
+        
+        public int getProvidersCount() {
+            return m_providers.size();
+        }
+        
+        public ServiceInterface getProvider(String name) {
+            for (ServiceReference ref : m_providers) {
+                Object n = ref.getProperty("name");
+                if (n.equals(name)) {
+                    return (ServiceInterface) m_bc.getService(ref);
+                }
+            }
+            return null;
+        }
+        
+        public void stop() {
+            m_stopped = true;
+        }
+        
+        public boolean isStopped() {
+            return m_stopped;
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterDependencyAddAndRemoveTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterDependencyAddAndRemoveTest.java
new file mode 100644
index 0000000..6d844d1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterDependencyAddAndRemoveTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.dm.itest.api;
+
+import java.net.URL;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentState;
+import org.apache.felix.dm.ComponentStateListener;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceAdapterDependencyAddAndRemoveTest extends TestBase {
+    public void testBasicResourceAdapter() throws Exception {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+
+        // create and add a service provider
+        m.add(m.createComponent()
+                .setInterface(ServiceInterface.class.getName(), null)
+                .setImplementation(new ServiceProvider(e)));
+        
+        // create and add a resource provider
+        ResourceProvider provider = new ResourceProvider(context, new URL("file://localhost/path/to/file1.txt"));
+        m.add(m.createComponent()
+                .setImplementation(provider)
+                .add(m.createServiceDependency()
+                    .setService(ResourceHandler.class)
+                    .setCallbacks("add", "remove"))
+                );
+        
+        // create a resource adapter for our single resource
+        // note that we can provide an actual implementation instance here because there will be only one
+        // adapter, normally you'd want to specify a Class here
+        // also, create a callback instance which will be used for both callbacks on resource changes and
+        // life cycle callbacks on the adapters themselves
+        
+        Dependency d = m.createServiceDependency()
+            .setService(ServiceInterface.class)
+            .setRequired(true);
+        CallbackInstance callbackInstance = new CallbackInstance(e, d);
+        Component component = m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, callbackInstance, "changed")
+            .setImplementation(new ResourceAdapter(e))
+            .setCallbacks(callbackInstance, "init", "start", "stop", "destroy");
+        
+        // add the resource adapter
+        m.add(component);
+        
+        // wait until the single resource is available (the adapter has been started)
+        e.waitForStep(1, 5000);
+        // trigger a 'change' in our resource
+        provider.change();
+        // wait until the changed callback is invoked
+        e.waitForStep(2, 5000);
+        // and has completed (ensuring no "extra" steps are invoked in the mean time)
+        e.waitForStep(3, 5000);
+                
+        // remove the resource adapter again
+        // add a component state listener, in order to track resource adapter destruction
+        component.add(new ComponentStateListenerImpl(e));
+        m.remove(component);
+        
+        // wait for the stopped callback in the state listener
+        e.waitForStep(4, 5000);
+        m.clear();
+     }
+    
+    static class ResourceAdapter {
+        protected URL m_resource; // injected by reflection.
+        
+        ResourceAdapter(Ensure e) {
+        }
+    }
+        
+    static interface ServiceInterface {
+        public void invoke();
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        public ServiceProvider(Ensure e) {
+        }
+        public void invoke() {
+        }
+    }    
+    
+    class CallbackInstance {
+        private final Dependency m_dependency;
+        private final Ensure m_ensure;
+        
+        
+        public CallbackInstance(Ensure e, Dependency d) {
+            m_ensure = e;
+            m_dependency = d;
+        }
+        
+        void init(Component c) {
+            debug("CallbackInstance.init");
+            c.add(m_dependency);
+        }
+        
+        void start() {
+            debug("CallbackInstance.start");
+            m_ensure.step(1);
+        }
+        
+        void stop() {
+            debug("CallbackInstance.stop");
+        }
+        
+        void destroy() {
+            debug("CallbackInstance.destroy");
+        }
+        
+        void changed(Component component) {
+            m_ensure.step(2);
+            Dependency oldDependency = m_dependency;
+            // and add a new dependency
+            component.add(component.getDependencyManager().createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+            // remove the old dependency
+            component.remove(oldDependency);
+            debug("CallbackInstance.changed the dependencies");
+            m_ensure.step(3);
+        }
+    }
+    
+    class ComponentStateListenerImpl implements ComponentStateListener {
+        
+        private final Ensure m_ensure;
+        
+        public ComponentStateListenerImpl(Ensure e) {
+            this.m_ensure = e;
+        }
+        
+        public void changed(Component c, ComponentState state) {
+            debug("ComponentStateListenerImpl.changed: state=%s", state);
+            switch (state) {
+            case INACTIVE:
+                System.out.println("stopped");
+                m_ensure.step(4);
+            default:
+            }
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterDependencyAddAndRemoveTest2.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterDependencyAddAndRemoveTest2.java
new file mode 100644
index 0000000..09d7dd1
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterDependencyAddAndRemoveTest2.java
@@ -0,0 +1,172 @@
+/*
+ * 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.dm.itest.api;
+
+import java.net.URL;
+import java.util.Hashtable;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentState;
+import org.apache.felix.dm.ComponentStateListener;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceAdapterDependencyAddAndRemoveTest2 extends TestBase {
+    public void testBasicResourceAdapter() throws Exception {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a resource provider
+        ResourceProvider provider = new ResourceProvider(context, new URL("file://localhost/path/to/file1.txt"));
+        // activate it
+        Hashtable<String, String> props = new Hashtable<String, String>();
+        props.put("id", "1");
+        m.add(m.createComponent()
+            .setInterface(ServiceInterface.class.getName(), props)
+            .setImplementation(new ServiceProvider(e))
+        );
+        
+        props = new Hashtable<String, String>();
+        props.put("id", "2");
+        m.add(m.createComponent()
+            .setInterface(ServiceInterface.class.getName(), props)
+            .setImplementation(new ServiceProvider(e))
+        );
+        
+        m.add(m.createComponent()
+            .setImplementation(provider)
+            .add(m.createServiceDependency()
+                .setService(ResourceHandler.class)
+                .setCallbacks("add", "remove")
+            )
+        );
+        
+        // create a resource adapter for our single resource
+        // note that we can provide an actual implementation instance here because there will be only one
+        // adapter, normally you'd want to specify a Class here
+        Dependency d = m.createServiceDependency().setService(ServiceInterface.class, "(id=1)").setRequired(true);
+        ResourceAdapter service = new ResourceAdapter(e, d);
+
+        CallbackInstance callbackInstance = new CallbackInstance(e, d);
+        Component component = m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, callbackInstance, "changed")
+            .setImplementation(service)
+            .setCallbacks(callbackInstance, "init", "start", "stop", "destroy");
+        component.add(new ComponentStateListenerImpl(e));
+        m.add(component);
+        // wait until the single resource is available
+        e.waitForStep(1, 5000);
+        // trigger a 'change' in our resource
+        provider.change();
+        // wait until the changed callback is invoked
+        e.waitForStep(2, 5000);
+        
+        System.out.println("Done!");
+        m.clear();
+     }
+    
+    static class ResourceAdapter {
+        protected URL m_resource; // injected by reflection.
+        final Dependency m_dependency;
+        
+        ResourceAdapter(Ensure e, Dependency d) {
+            m_dependency = d;
+        }
+    }
+        
+    static interface ServiceInterface {
+        public void invoke();
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        public ServiceProvider(Ensure e) {
+        }
+        public void invoke() {
+        }
+    }    
+    
+    static class CallbackInstance {
+        private final Ensure m_ensure;
+        private final Dependency m_dependency;
+        
+        public CallbackInstance(Ensure e, Dependency d) {
+            m_ensure = e;
+            m_dependency = d;
+        }
+        
+        void init(Component c) {
+            c.add(m_dependency);
+            System.out.println("init");
+            m_ensure.step(1);
+        }
+        
+        void start() {
+            System.out.println("start");
+        }
+        
+        void stop() {
+            System.out.println("stop");
+        }
+        
+        void destroy() {
+            System.out.println("destroy");
+        }
+        
+        void changed(Component component) {
+            m_ensure.step(2);
+            System.out.println("Changing the dependencies");
+            Dependency oldDependency = m_dependency;
+            
+            // and add a new dependency
+            component.add(component.getDependencyManager().createServiceDependency().setService(ServiceInterface.class, "(id=2)").setRequired(true));
+            // remove the old dependency
+            component.remove(oldDependency);
+            System.out.println("Changed the dependencies");
+        }
+    }
+    
+    static class ComponentStateListenerImpl implements ComponentStateListener {
+        public ComponentStateListenerImpl(Ensure e) {
+        }
+
+        @Override
+        public void changed(Component c, ComponentState state) {
+            switch (state) {
+            case INACTIVE:
+                System.out.println("INACTIVE");
+                break;
+            case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+                System.out.println("INSTANTIATED_AND_WAITING_FOR_REQUIRED");
+                break;
+            case WAITING_FOR_REQUIRED:
+                System.out.println("WAITING_FOR_REQUIRED");
+                break;
+            case TRACKING_OPTIONAL:
+                System.out.println("TRACKING_OPTIONAL");
+                break;
+
+            }
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterTest.java
new file mode 100644
index 0000000..ad25a54
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceAdapterTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.dm.itest.api;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.junit.Assert;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceAdapterTest extends TestBase {
+    public void testBasicResourceAdapter() throws Exception {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a resource provider
+        ResourceProvider provider = new ResourceProvider(context, new URL("file://localhost/path/to/file1.txt"));
+        // activate it
+        m.add(m.createComponent().setImplementation(provider).add(m.createServiceDependency().setService(ResourceHandler.class).setCallbacks("add", "remove")));
+        // create a resource adapter for our single resource
+        // note that we can provide an actual implementation instance here because there will be only one
+        // adapter, normally you'd want to specify a Class here
+        m.add(m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, null, "changed")
+              .setImplementation(new ResourceAdapter(e)));
+        // wait until the single resource is available
+        e.waitForStep(3, 5000);
+        // trigger a 'change' in our resource
+        provider.change();
+        // wait until the changed callback is invoked
+        e.waitForStep(4, 5000);
+        m.clear();
+     }
+    
+    static class ResourceAdapter {
+        protected URL m_resource; // injected by reflection.
+        private Ensure m_ensure;
+        
+        ResourceAdapter(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void start() {
+            m_ensure.step(1);
+            Assert.assertNotNull("resource not injected", m_resource);
+            m_ensure.step(2);
+            try {
+                m_resource.openStream();
+            } 
+            catch (FileNotFoundException e) {
+                m_ensure.step(3);
+            }
+            catch (IOException e) {
+                Assert.fail("We should not have gotten this exception.");
+            }
+        }
+        
+        public void changed() {
+            m_ensure.step(4);
+        }
+    }    
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceDependencyTest.java
new file mode 100644
index 0000000..9595a9f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceDependencyTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.dm.itest.api;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.junit.Assert;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceDependencyTest extends TestBase {
+    public void testResourceDependency() throws MalformedURLException {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        ResourceConsumer c = new ResourceConsumer(e);
+        Component consumer = m.createComponent()
+            .setImplementation(c)
+            .add(m.createResourceDependency()
+                .setFilter("(&(path=/path/to/*.txt)(host=localhost))")
+                .setCallbacks("add", "change", "remove"));
+        Component dynamicProxyConsumer = m.createComponent()
+            .setFactory(new ResourceConsumerFactory(e), "create")
+            .add(m.createResourceDependency()
+                    .setFilter("(path=*.doc)")
+                    .setCallbacks("add", null)); 
+        ResourceProvider provider = new ResourceProvider(context, 
+        		new URL("file://localhost/path/to/file1.txt"),
+                new URL("file://localhost/path/to/file2.txt"),
+                new URL("file://localhost/path/to/file3.doc"));
+        Component resourceProvider = m.createComponent()
+            .setImplementation(provider)
+            .add(m.createServiceDependency()
+                .setService(ResourceHandler.class)
+                .setCallbacks("add", "remove"));
+        
+        // first add the consumer
+        m.add(consumer);
+        // then the resource provider, which will provide 3 resources,
+        // 2 of which match the consumers filter conditions
+        m.add(resourceProvider);
+        // make sure our consumer invoked openStream() on both resources,
+        // increasing the step counter to 2
+        e.step(3);
+        
+        // now add another consumer, that matches only one resource, and uses
+        // a dynamic proxy as its implementation
+        m.add(dynamicProxyConsumer);
+        // ensure the resource was injected properly
+        e.waitForStep(4, 5000);
+        
+        // now change a resource and see if it gets propagated to the consumer
+        provider.change(0);
+        
+        // wait for change callback
+        e.waitForStep(5, 5000);
+        e.step(6);
+        
+        // cleanup
+        m.remove(dynamicProxyConsumer);
+        m.remove(resourceProvider);
+        m.remove(consumer);
+        
+        // validate that all consumed resources are "unconsumed" again
+        c.ensure();
+        m.clear();
+    }
+    
+    class ResourceConsumer {
+        private volatile int m_counter;
+        private Ensure m_ensure;
+        
+        public ResourceConsumer(Ensure ensure) {
+            m_ensure = ensure;
+        }
+        
+        public void add(URL resource) {
+            debug("ResourceConsumer.add(%s)", resource);
+            m_counter++;
+            m_ensure.step();
+        }
+        public void change(URL resource) {
+            m_ensure.step();
+        }
+        public void remove(URL resource) {
+            debug("ResourceConsumer.remove(%s)", resource);
+            m_counter--;
+        }
+        public void ensure() {
+            Assert.assertTrue("all resources should have been added and removed at this point, but " + m_counter + " are remaining", m_counter == 0);
+        }
+    }
+    
+    
+    class ResourceConsumerFactory {
+        private final Ensure m_ensure;
+        public ResourceConsumerFactory(Ensure ensure) {
+            m_ensure = ensure;
+        }
+        public Object create() {
+            ResourceConsumer resourceConsumer = new ResourceConsumer(m_ensure);
+            // create a dynamic proxy for the ResourceProvider
+            return Proxy.newProxyInstance(resourceConsumer.getClass().getClassLoader(), resourceConsumer.getClass().getInterfaces(), new DynamicProxyHandler(resourceConsumer, m_ensure));
+        }
+    }
+
+    static class DynamicProxyHandler implements InvocationHandler {
+        Ensure m_ensure;
+        ResourceConsumer resourceConsumer = null;
+        
+        public DynamicProxyHandler(ResourceConsumer resourceConsumer, Ensure ensure) {
+            this.resourceConsumer = resourceConsumer;
+            m_ensure = ensure;
+        }
+
+        public void add(URL resource) {
+            m_ensure.step(4);
+        }
+
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            return method.invoke(resourceConsumer, args);
+        }
+    } 
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceProvider.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceProvider.java
new file mode 100644
index 0000000..7e0e46b
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ResourceProvider.java
@@ -0,0 +1,121 @@
+/*
+ * 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.dm.itest.api;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.ResourceUtil;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+class ResourceProvider {
+	final URL[] m_resources;
+    final BundleContext m_context;
+    final Map<ResourceHandler, Filter> m_handlers = new HashMap<>();
+
+	ResourceProvider(BundleContext ctx, URL ... resources) {
+		m_context = ctx;
+		m_resources = resources;
+	}
+	
+    public void change() {
+        for (int i = 0; i < m_resources.length; i++) {
+        	change(i);
+        }    	
+    }
+    
+    @SuppressWarnings("deprecation")
+	public void change(int resourceIndex) {
+        Map<ResourceHandler, Filter> handlers = new HashMap<>();
+        synchronized (m_handlers) {
+            handlers.putAll(m_handlers);
+        }
+        for (Map.Entry<ResourceHandler, Filter> e : handlers.entrySet()) {
+        	ResourceHandler handler = e.getKey();
+        	Filter filter = e.getValue();
+        	if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[resourceIndex]))) {
+        		handler.changed(m_resources[resourceIndex]);
+            }
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+	public void add(ServiceReference ref, ResourceHandler handler) {
+        String filterString = (String) ref.getProperty("filter");
+        Filter filter = null;
+        if (filterString != null) {
+            try {
+                filter = m_context.createFilter(filterString);
+            }
+            catch (InvalidSyntaxException e) {
+                Assert.fail("Could not create filter for resource handler: " + e);
+                return;
+            }
+        }
+        for (int i = 0; i < m_resources.length; i++) {
+            if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+                synchronized (m_handlers) {
+                    m_handlers.put(handler, filter);
+                }
+                handler.added(m_resources[i]);
+            }
+        }
+    }
+
+    public void remove(ServiceReference ref, ResourceHandler handler) {
+        Filter filter;
+        synchronized (m_handlers) {
+            filter = (Filter) m_handlers.remove(handler);
+        }
+        if (filter != null) {
+        	removeResources(handler, filter);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+	private void removeResources(ResourceHandler handler, Filter filter) {
+            for (int i = 0; i < m_resources.length; i++) {
+                if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+                    handler.removed(m_resources[i]);
+                }
+            }
+        }
+
+    public void destroy() {
+        Map<ResourceHandler, Filter> handlers = new HashMap<>();
+        synchronized (m_handlers) {
+            handlers.putAll(m_handlers);
+        }
+
+        for (Map.Entry<ResourceHandler, Filter> e : handlers.entrySet()) {
+            ResourceHandler handler = e.getKey();
+            Filter filter = e.getValue();
+            removeResources(handler, filter);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyCallbackSignaturesTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyCallbackSignaturesTest.java
new file mode 100644
index 0000000..e39f830
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyCallbackSignaturesTest.java
@@ -0,0 +1,294 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings("unused")
+public class ServiceDependencyCallbackSignaturesTest extends TestBase {
+    volatile Ensure m_ensure;
+    
+    /**
+     * Tests if all possible dependency callbacks signatures supported by ServiceDependency.
+     */
+    public void testDependencyCallbackSignatures() {
+        DependencyManager m = getDM();
+        m_ensure = new Ensure();    
+        Hashtable<String, String> props = new Hashtable<>();
+        props.put("foo", "bar");
+        Component provider = m.createComponent()
+            .setImplementation(new ProviderImpl()).setInterface(Provider.class.getName(), props);
+        
+        declareConsumer(m, new Object() {
+            void bind(Component c, ServiceReference ref, Provider provider) {
+                Assert.assertNotNull(c);
+                Assert.assertNotNull(ref);
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("bar", ref.getProperty("foo"));
+                Assert.assertEquals(context.getService(ref), provider);
+                m_ensure.step();
+            }
+            void change(Component c, ServiceReference ref, Provider provider) {
+                Assert.assertNotNull(c);
+                Assert.assertNotNull(ref);
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("zoo", ref.getProperty("foo"));
+                Assert.assertEquals(context.getService(ref), provider);
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(Component c, ServiceReference ref, Object provider) {
+                Assert.assertNotNull(c);
+                Assert.assertNotNull(ref);
+                Assert.assertEquals("bar", ref.getProperty("foo"));
+                Assert.assertNotNull(provider);
+                Assert.assertEquals(context.getService(ref), provider);
+                m_ensure.step();
+            }
+            void change(Component c, ServiceReference ref, Object provider) {
+                Assert.assertNotNull(c);
+                Assert.assertNotNull(ref);
+                Assert.assertEquals("zoo", ref.getProperty("foo"));
+                Assert.assertNotNull(provider);
+                Assert.assertEquals(context.getService(ref), provider);
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(Component c, ServiceReference ref) {
+                Assert.assertNotNull(c);
+                Assert.assertNotNull(ref);
+                Assert.assertEquals("bar", ref.getProperty("foo"));
+                Assert.assertNotNull(context.getService(ref));
+                Assert.assertEquals(context.getService(ref).getClass(), ProviderImpl.class);
+                m_ensure.step();
+            }
+            void change(Component c, ServiceReference ref) {
+                Assert.assertNotNull(c);
+                Assert.assertNotNull(ref);
+                Assert.assertEquals("zoo", ref.getProperty("foo"));
+                Assert.assertNotNull(context.getService(ref));
+                Assert.assertEquals(context.getService(ref).getClass(), ProviderImpl.class);
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(Component c, Provider provider) {
+                Assert.assertNotNull(c);
+                Assert.assertNotNull(provider);
+                m_ensure.step();
+            }
+            void change(Component c, Provider provider) {
+                Assert.assertNotNull(c);
+                Assert.assertNotNull(provider);
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(Component c, Object provider) {
+                Assert.assertNotNull(c);
+                Assert.assertNotNull(provider);
+                Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+                m_ensure.step();
+            }
+            void change(Component c, Object provider) {
+                Assert.assertNotNull(c);
+                Assert.assertNotNull(provider);
+                Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(Component c) {
+                Assert.assertNotNull(c);
+                m_ensure.step();
+            }
+            void change(Component c) {
+                Assert.assertNotNull(c);
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(Component c, Map<String, String> props, Provider provider) {
+                Assert.assertNotNull(c);
+                Assert.assertNotNull(props);
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("bar", props.get("foo"));
+                Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+                m_ensure.step();
+            }
+            void change(Component c, Map<String, String> props, Provider provider) {
+                Assert.assertNotNull(c);
+                Assert.assertNotNull(props);
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("zoo", props.get("foo"));
+                Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(ServiceReference ref, Provider provider) {
+                Assert.assertNotNull(ref);
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("bar", ref.getProperty("foo"));
+                m_ensure.step();
+            }
+            void change(ServiceReference ref, Provider provider) {
+                Assert.assertNotNull(ref);
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("zoo", ref.getProperty("foo"));
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(ServiceReference ref, Object provider) {
+                Assert.assertNotNull(ref);
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("bar", ref.getProperty("foo"));
+                Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+                m_ensure.step();
+            }
+            void change(ServiceReference ref, Object provider) {
+                Assert.assertNotNull(ref);
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("zoo", ref.getProperty("foo"));
+                Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(ServiceReference ref) {
+                Assert.assertNotNull(ref);
+                Assert.assertEquals("bar", ref.getProperty("foo"));
+                m_ensure.step();
+            }
+            void change(ServiceReference ref) {
+                Assert.assertNotNull(ref);
+                Assert.assertEquals("zoo", ref.getProperty("foo"));
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(Provider provider) {
+                Assert.assertNotNull(provider);
+                m_ensure.step();
+            }
+            void change(Provider provider) {
+                Assert.assertNotNull(provider);
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(Provider provider, Map<?, ?> props) {
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("bar", props.get("foo"));
+                m_ensure.step();
+            }
+            void change(Provider provider, Map<?, ?> props) {
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("zoo", props.get("foo"));
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(Map<?, ?> props, Provider provider) {
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("bar", props.get("foo"));
+                m_ensure.step();
+            }
+            void change(Map<?, ?> props, Provider provider) {
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("zoo", props.get("foo"));
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(Provider provider, Dictionary<?, ?> props) {
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("bar", props.get("foo"));
+                m_ensure.step();
+            }
+            void change(Provider provider, Dictionary<?, ?> props) {
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("zoo", props.get("foo"));
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(Dictionary<?, ?> props, Provider provider) {
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("bar", props.get("foo"));
+                m_ensure.step();
+            }
+            void change(Dictionary<?, ?> props, Provider provider) {
+                Assert.assertNotNull(provider);
+                Assert.assertEquals("zoo", props.get("foo"));
+                m_ensure.step();
+            }
+        });
+        declareConsumer(m, new Object() {
+            void bind(Object provider) {
+                Assert.assertNotNull(provider);
+                Assert.assertEquals(provider.getClass(), ProviderImpl.class);
+                m_ensure.step();
+            }
+            void change(Object provider) {
+                bind(provider);
+            }
+        });
+
+        m.add(provider);
+        m_ensure.waitForStep(16, 5000);
+        
+        props = new Hashtable<>();
+        props.put("foo", "zoo");
+        provider.setServiceProperties(props);
+        m_ensure.waitForStep(32, 5000);
+        
+        m.remove(provider);
+        m_ensure.waitForStep(48, 5000);
+    }
+    
+    private void declareConsumer(DependencyManager m, Object consumerImpl) {
+        Component consumer = m.createComponent().setImplementation(consumerImpl)
+            .add(m.createServiceDependency().setService(Provider.class).setCallbacks("bind", "change", "change").setRequired(true));
+        m.add(consumer);
+    }
+
+    public static interface Provider {        
+    }
+    
+    public static class ProviderImpl implements Provider {        
+    }   
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyInjectionTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyInjectionTest.java
new file mode 100644
index 0000000..47581ff
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyInjectionTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.dm.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceDependencyInjectionTest extends TestBase {
+    public void testServiceInjection() {
+        DependencyManager m = getDM();
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        ServiceProvider provider = new ServiceProvider(e);
+        Component sp = m.createComponent().setImplementation(provider).setInterface(ServiceInterface2.class.getName(), null);
+        Component sc = m.createComponent().setImplementation(new ServiceConsumer())
+            .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(true));
+        Component sc2 = m.createComponent() // all dependencies are optional
+            .setImplementation(new ServiceConsumerNamedInjection(false, false)) 
+            .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service"))
+            .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service2"))
+            .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service3"))
+            ;
+        Component sc3 = m.createComponent() // second dependency is required, first and third are optional
+            .setImplementation(new ServiceConsumerNamedInjection(false, false))
+            .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service"))
+            .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(true).setAutoConfig("m_service2"))
+            .add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service3"))
+            ;
+        Component sc4 = m.createComponent()
+            .setImplementation(new ServiceConsumerNamedInjection(true, false));
+        Component sc5 = m.createComponent()
+            .setImplementation(new ServiceConsumerNamedInjection(true, true));
+        m.add(sp);
+        m.add(sc);
+        m.remove(sc);
+        m.add(sc2);
+        m.remove(sc2);
+        m.add(sc3);
+        m.remove(sc4);
+        m.add(sc4);
+        m.remove(sc4);
+        m.add(sc5);
+        m.remove(sc5);
+        m.remove(sp);
+        e.waitForStep(11, 5000);
+        m.clear();
+    }
+    
+    static interface ServiceInterface {
+        public void invoke();
+    }
+    
+    static interface ServiceInterface2 extends ServiceInterface {
+        public void invoke2();
+    }
+
+    static class ServiceProvider implements ServiceInterface2 {
+        private final Ensure m_ensure;
+        private Ensure.Steps m_invokeSteps = new Ensure.Steps(4, 5, 7, 8, 10, 11, 13, 14);
+        private Ensure.Steps m_invoke2Steps = new Ensure.Steps(1, 2, 3, 6, 9, 12);
+        
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+
+        public void invoke() {
+            System.out.println("invoke");
+            m_ensure.steps(m_invokeSteps);
+        }
+        
+        public void invoke2() {
+            System.out.println("invoke2");
+            m_ensure.steps(m_invoke2Steps);
+        }
+    }
+
+    static class ServiceConsumer {
+        private volatile ServiceInterface2 m_service;
+        private volatile ServiceInterface2 m_service2;
+        
+        public void init() {
+            // invoke the second method of the interface via both injected members, to ensure
+            // neither of them is a null object (or null)
+            m_service.invoke2();
+            m_service2.invoke2();
+            Assert.assertEquals("Both members should have been injected with the same service.", m_service, m_service2);
+        }
+    }
+
+    class ServiceConsumerNamedInjection {
+        private volatile ServiceInterface2 m_service;
+        private volatile ServiceInterface m_service2;
+        private volatile Object m_service3;
+        private final boolean m_secondDependencyRequired;
+        private final boolean m_instanceBound;
+        
+        ServiceConsumerNamedInjection(boolean instanceBound, boolean withSecondRequired) {
+            m_secondDependencyRequired = withSecondRequired;
+            m_instanceBound = instanceBound;
+        }
+
+        public void init(Component c) {
+            if (m_instanceBound) {
+                DependencyManager m = c.getDependencyManager();
+                c.add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service"),
+                    m.createServiceDependency().setService(ServiceInterface2.class).setRequired(m_secondDependencyRequired).setAutoConfig("m_service2"),
+                    m.createServiceDependency().setService(ServiceInterface2.class).setRequired(false).setAutoConfig("m_service3"));
+            } else {
+                check();
+            }
+        }
+        
+        public void start() {
+            if (m_instanceBound) {
+                check();
+            }
+        }
+        
+        public void check() {
+            warn("ServiceConsumerNamedInjectionInstanceBound: m_service=%s, m_service2=%s, m_service3=%s", m_service, m_service2, m_service3);
+            // invoke the second method
+            m_service.invoke2();
+            // invoke the first method (twice)
+            m_service2.invoke();
+            ((ServiceInterface) m_service3).invoke();
+            Assert.assertNotNull("Should have been injected", m_service);
+            Assert.assertNotNull("Should have been injected", m_service2);
+            Assert.assertNotNull("Should have been injected", m_service3);
+            Assert.assertEquals("Members should have been injected with the same service.", m_service, m_service2);
+            Assert.assertEquals("Members should have been injected with the same service.", m_service, m_service3);          
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyPropagateTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyPropagateTest.java
new file mode 100644
index 0000000..2efb898
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyPropagateTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Validates ServiceDependency service properties propagation.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class ServiceDependencyPropagateTest extends TestBase {
+    /**
+     * Checks that a ServiceDependency propagates the dependency service properties to the provided service properties.
+     */
+    public void testServiceDependencyPropagate() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        Component c1 = m.createComponent()
+                      .setImplementation(new C1(e))
+                      .add(m.createServiceDependency().setService(C2.class).setRequired(true).setCallbacks("bind", null));
+
+        Component c2 = m.createComponent()
+                      .setInterface(C2.class.getName(), new Hashtable() {{ put("foo", "bar"); }})
+                      .setImplementation(new C2())
+                      .add(m.createServiceDependency().setService(C3.class).setRequired(true).setPropagate(true));
+
+        Component c3 = m.createComponent()
+                      .setInterface(C3.class.getName(), new Hashtable() {{ put("foo2", "bar2"); put("foo", "overriden");}})
+                      .setImplementation(new C3());
+        
+        m.add(c1);
+        m.add(c2);
+        m.add(c3);
+
+        e.waitForStep(3, 10000);
+        
+        m.remove(c3);
+        m.remove(c2);
+        m.remove(c1);
+        m.clear();
+    }
+    
+    /**
+     * Checks that a ServiceDependency propagates the dependency service properties to the provided service properties,
+     * using a callback method.
+     */
+    public void testServiceDependencyPropagateCallback() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        Component c1 = m.createComponent()
+                      .setImplementation(new C1(e))
+                      .add(m.createServiceDependency().setService(C2.class).setRequired(true).setCallbacks("bind", null));
+
+        C2 c2Impl = new C2();
+        Component c2 = m.createComponent()
+                      .setInterface(C2.class.getName(), new Hashtable() {{ put("foo", "bar"); }})
+                      .setImplementation(c2Impl)
+                      .add(m.createServiceDependency().setService(C3.class).setRequired(true).setPropagate(c2Impl, "getServiceProperties"));
+        
+        Component c3 = m.createComponent()
+                      .setInterface(C3.class.getName(), null)
+                      .setImplementation(new C3());
+        
+        m.add(c1);
+        m.add(c2);
+        m.add(c3);
+
+        e.waitForStep(3, 10000);
+        m.clear();
+    }
+    
+    public static class C1 {
+        private Map m_props;
+        private Ensure m_ensure;
+        
+        C1(Ensure ensure) {
+            m_ensure = ensure;
+        }
+
+        void bind(Map props, C2 c2) {
+            m_props = props;
+        }
+        
+        void start() {
+            m_ensure.step(1);
+            if ("bar".equals(m_props.get("foo"))) { // "foo=overriden" from C2 should not override our own "foo" property
+                m_ensure.step(2);
+            }
+            if ("bar2".equals(m_props.get("foo2"))) {
+                m_ensure.step(3);
+            }
+        }
+    }
+    
+    public static class C2 {
+      C3 m_c3;
+      
+      public Dictionary getServiceProperties(ServiceReference ref) {
+          return new Hashtable() {{ put("foo2", "bar2"); put("foo", "overriden"); }};
+      }
+    }
+    
+    public static class C3 {
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyTest.java
new file mode 100644
index 0000000..ae0b2ee
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.dm.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceDependencyTest extends TestBase {
+    public void testServiceRegistrationAndConsumption() {
+        DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Component sp = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sc = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+        Component sc2 = m.createComponent().setImplementation(new ServiceConsumerCallbacks(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(false).setCallbacks("add", "remove"));
+        m.add(sp);
+        m.add(sc);
+        m.remove(sc);
+        m.add(sc2);
+        m.remove(sp);
+        m.remove(sc2);
+        // ensure we executed all steps inside the component instance
+        e.step(6);
+    }
+    
+    static interface ServiceInterface {
+        public void invoke();
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke() {
+            m_ensure.step(2);
+        }
+    }
+
+    static class ServiceConsumer {
+        private volatile ServiceInterface m_service;
+        private final Ensure m_ensure;
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            m_ensure.step(1);
+            m_service.invoke();
+        }
+        
+        public void destroy() {
+            m_ensure.step(3);
+        }
+    }
+
+    static class ServiceConsumerCallbacks {
+        private final Ensure m_ensure;
+
+        public ServiceConsumerCallbacks(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void add(ServiceInterface service) {
+            m_ensure.step(4);
+        }
+        public void remove(ServiceInterface service) {
+            m_ensure.step(5);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyThroughCallbackInstanceTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyThroughCallbackInstanceTest.java
new file mode 100644
index 0000000..e376e29
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceDependencyThroughCallbackInstanceTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.dm.itest.api;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.BundleContext;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceDependencyThroughCallbackInstanceTest extends TestBase {
+    public void testServiceWithCallbacksAndOneDependency() {
+        invokeTest(context, 1);
+    }
+    
+    public void testServiceWithCallbacksAndThreeDependencies() {
+        invokeTest(context, 3);
+    }
+
+    private void invokeTest(BundleContext context, int numberOfServices) {
+        DependencyManager m = getDM();
+        // create a number of services
+		for (int i = 0; i < numberOfServices; i++) {
+			final int num = i;
+			m.add(m.createComponent()
+			    .setInterface(Service.class.getName(), null)
+			    .setImplementation(new Service() {
+			        public String toString() {
+			            return "A" + num;
+			            }
+			        }
+			    )
+		    );
+		}
+
+		// create a service with dependency which will be invoked on a callback instance
+		CallbackInstance instance = new CallbackInstance();
+		m.add(m.createComponent()
+		    .setImplementation(new SimpleService() {})
+		    .add(m.createServiceDependency()
+				.setService(Service.class)
+				.setCallbacks(instance, "added", "removed")
+				.setRequired(true)
+			)
+		);
+		
+		Assert.assertEquals(numberOfServices, instance.getCount());
+		m.clear();
+    }
+    
+    public static interface Service {
+    }
+    
+    public static interface SimpleService {
+    }
+    
+    public static class CallbackInstance {
+    	int m_count = 0;
+
+    	void added(Service service) {
+    	    System.out.println("added " + service);
+    		m_count++;
+    	}
+    	
+    	void removed(Service service) {
+    	}	
+    	
+    	int getCount() {
+    		return m_count;
+    	}
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceParallelTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceParallelTest.java
new file mode 100644
index 0000000..ba97fbe
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceParallelTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.dm.itest.api;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceRaceParallelTest extends ServiceRaceTest {
+    public ServiceRaceParallelTest() {
+        setParallel(); // Configure DM to use a threadpool
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java
new file mode 100644
index 0000000..bcc6b17
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java
@@ -0,0 +1,306 @@
+/*
+ * 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.dm.itest.api;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ConfigurationDependency;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationException;
+
+
+/**
+ * This test class simulates a client having many dependencies being registered/unregistered concurrently.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class ServiceRaceTest extends TestBase {
+    volatile ConfigurationAdmin m_cm;
+    final static int STEP_WAIT = 5000;
+    final static int DEPENDENCIES = 10;
+    final static int LOOPS = 3000;
+    final Ensure m_done = new Ensure(true);
+
+    // Executor used to bind/unbind service dependencies.
+    ExecutorService m_threadpool;
+    
+    // Timestamp used to log the time consumed to execute 100 tests.
+    long m_timeStamp;
+    
+    public interface Dep {        
+    }
+    
+    public class DepImpl implements Dep {        
+    }
+    
+    /**
+     * Creates many service dependencies, and activate/deactivate them concurrently.  
+     */
+    public void testCreateParallelComponentRegistgrationUnregistration() {
+        m_dm.add(m_dm.createComponent()
+            .setImplementation(this)
+            .setCallbacks(null, "start", null, null)
+            .add(m_dm.createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true)));
+        m_done.waitForStep(1, 60000);
+        m_dm.clear();
+        Assert.assertFalse(super.errorsLogged());
+    }
+    
+    void start() {
+        new Thread(new Runnable() {
+            public void run() {
+                doStart();
+            }}).start();
+    }
+    
+    void doStart() {
+        info("Starting createParallelComponentRegistgrationUnregistration test");
+        initThreadPool(); // only if setParallel() has not been called (only if a parallel DM is not used).
+
+        try {
+            m_timeStamp = System.currentTimeMillis();
+            for (int loop = 0; loop < LOOPS; loop++) {
+                doTest(loop);
+            }
+        }
+        catch (Throwable t) {
+            error("got unexpected exception", t);
+        }
+        finally {
+            shutdownThreadPool();
+            m_done.step(1);
+        }
+    }
+
+    private void initThreadPool() {
+        if (! m_parallel) { 
+            // We are not using a parallel DM, so we create a custom threadpool in order to add components concurrently.
+            int cores = Math.max(16, Runtime.getRuntime().availableProcessors());
+            info("using " + cores + " cores.");
+            m_threadpool = Executors.newFixedThreadPool(Math.max(cores, DEPENDENCIES + 3 /* start/stop/configure */));
+        }
+    }
+
+    void shutdownThreadPool() {
+        if (! m_parallel && m_threadpool != null) {
+            m_threadpool.shutdown();
+            try {
+                m_threadpool.awaitTermination(60, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
+    void doTest(int loop) throws Throwable {
+        debug("loop#%d -------------------------", loop);
+
+        final Ensure step = new Ensure(false);
+
+        // Create one client component, which depends on many service dependencies
+        final Component client = m_dm.createComponent();
+        final Client clientImpl = new Client(step);
+        client.setImplementation(clientImpl);
+
+        // Create client service dependencies
+        final ServiceDependency[] dependencies = new ServiceDependency[DEPENDENCIES];
+        for (int i = 0; i < DEPENDENCIES; i++) {
+            final String filter = "(id=loop" + loop + "." + i + ")";
+            dependencies[i] = m_dm.createServiceDependency().setService(Dep.class, filter)
+                .setRequired(true)
+                .setCallbacks("add", "remove");
+            client.add(dependencies[i]);
+        }
+        String pid = "pid." + loop;
+        final ConfigurationDependency confDependency = m_dm.createConfigurationDependency().setPid(pid);
+        client.add(confDependency);
+
+        // Create Configuration (concurrently).
+        final Configuration conf = m_cm.getConfiguration(pid, null);
+        final Hashtable props = new Hashtable();
+        props.put("foo", "bar");
+        schedule(new Runnable() {
+            public void run() {
+                try {
+                    conf.update(props);
+                }
+                catch (IOException e) {
+                    error("update failed", e);
+                }
+            }
+        });
+        
+        // Activate the client service dependencies concurrently.
+        List<Component> deps = new ArrayList();
+        for (int i = 0; i < DEPENDENCIES; i++) {
+            Hashtable h = new Hashtable();
+            h.put("id", "loop" + loop + "." + i);
+            final Component s = m_dm.createComponent()
+                .setInterface(Dep.class.getName(), h)
+                .setImplementation(new DepImpl());
+            deps.add(s);
+            schedule(new Runnable() {
+                public void run() {
+                    m_dm.add(s);
+                }
+            });
+        }
+
+        // Start the client (concurrently)
+        schedule(new Runnable() {
+            public void run() {
+                m_dm.add(client);
+            }
+        });
+
+        // Ensure that client has been started.
+        int expectedStep = 1 /* conf */ + DEPENDENCIES + 1 /* start */;
+        step.waitForStep(expectedStep, STEP_WAIT);
+        Assert.assertEquals(DEPENDENCIES, clientImpl.getDependencies());
+        Assert.assertNotNull(clientImpl.getConfiguration());
+
+        // Stop all dependencies concurrently.
+        for (Component dep : deps) {
+            final Component dependency = dep;
+            schedule(new Runnable() {
+                public void run() {
+                    m_dm.remove(dependency);
+                }
+            });
+        }
+        
+        // Stop client concurrently.
+        schedule(new Runnable() {
+            public void run() {
+                m_dm.remove(client);
+            }
+        });
+        
+        // Remove configuration (asynchronously)
+        schedule(new Runnable() {
+            public void run() {
+                try {
+                    conf.delete();
+                }
+                catch (IOException e) {
+                    warn("error while unconfiguring", e);
+                }
+            }
+        });
+
+        // Ensure that client has been stopped, then destroyed, then unbound from all dependencies
+        expectedStep += 2; // stop/destroy
+        expectedStep += DEPENDENCIES; // removed all dependencies
+        expectedStep += 1; // removed configuration
+        step.waitForStep(expectedStep, STEP_WAIT);
+        step.ensure();
+        Assert.assertEquals(0, clientImpl.getDependencies());
+        Assert.assertNull(clientImpl.getConfiguration());                
+
+        if (super.errorsLogged()) {
+            throw new IllegalStateException("Race test interrupted (some error occured, see previous logs)");
+        }
+
+        debug("finished one test loop");
+        if ((loop + 1) % 100 == 0) {
+            long duration = System.currentTimeMillis() - m_timeStamp;
+            warn("Performed 100 tests (total=%d) in %d ms.", (loop + 1), duration);
+            m_timeStamp = System.currentTimeMillis();
+        }
+    }
+
+    private void schedule(Runnable task) {
+        if (! m_parallel) {
+            // not using parallel DM, so use our custom threadpool.
+            m_threadpool.execute(task);
+        } else {
+            task.run();
+        }        
+    }
+
+    public class Client {
+        final Ensure m_step;
+        volatile int m_dependencies;
+        volatile Dictionary m_conf;
+        
+        public Client(Ensure step) {
+            m_step = step;
+        }
+
+        public void updated(Dictionary conf) throws ConfigurationException {
+            m_conf = conf;
+            try {
+                if (conf != null) {
+                    Assert.assertEquals("bar", conf.get("foo"));
+                    m_step.step(1);
+                } else {
+                    m_step.step();
+                }
+            } catch (Throwable t) {
+                m_step.throwable(t);
+            }
+        }
+        
+        void add(Dep d) {
+            Assert.assertNotNull(d);
+            m_step.step();
+            m_dependencies ++;
+        }
+        
+        void remove(Dep d) {
+            Assert.assertNotNull(d);
+            m_step.step();
+            m_dependencies --;
+        }
+                
+        void start() {
+            m_step.step((DEPENDENCIES + 1) /* deps + conf */ + 1 /* start */);
+        }
+
+        void stop() {
+            m_step.step((DEPENDENCIES + 1) /* deps + conf */ + 1 /* start */ + 1 /* stop */);
+        }
+        
+        void destroy() {
+            m_step.step((DEPENDENCIES + 1) /* deps + conf */ + 1 /* start */ + 1 /* stop */  + 1 /* destroy */);
+        }
+        
+        int getDependencies() {
+            return m_dependencies;
+        }
+        
+        Dictionary getConfiguration() {
+            return m_conf;
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceTrackerTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceTrackerTest.java
new file mode 100644
index 0000000..d9076b4
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceTrackerTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Hashtable;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.ServiceUtil;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class ServiceTrackerTest extends TestBase {
+    public void testPlainServiceTracker() {
+        ServiceTracker st = new ServiceTracker(context, ServiceInterface.class.getName(), null);
+        st.open();
+        ServiceRegistration sr = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(), null);
+        Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+        sr.unregister();
+        Assert.assertNull("There should be no service that matches the tracker", st.getServices());
+        st.close();
+    }
+    
+    public void testAspectServiceTracker() {
+        ServiceTracker st = new ServiceTracker(context, ServiceInterface.class.getName(), null);
+        st.open();
+
+        ServiceRegistration sr = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(), null);
+        Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+        
+        final long sid = ServiceUtil.getServiceId(sr.getReference());
+        ServiceRegistration asr = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(),
+            new Hashtable() {{ put(DependencyManager.ASPECT, sid); put(Constants.SERVICE_RANKING, 10); }});
+        Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+        Assert.assertEquals("Service ranking should be 10", Integer.valueOf(10), (Integer) st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+
+        ServiceRegistration asr2 = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(),
+            new Hashtable() {{ put(DependencyManager.ASPECT, sid); put(Constants.SERVICE_RANKING, 20); }});
+        Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+        Assert.assertEquals("Service ranking should be 20", Integer.valueOf(20), (Integer) st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+        
+        asr.unregister();
+        Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+        Assert.assertEquals("Service ranking should be 20", Integer.valueOf(20), (Integer) st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+        
+        asr2.unregister();
+        Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+        Assert.assertNull("Service should not have a ranking", st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+        
+        sr.unregister();
+        Assert.assertNull("There should be no service that matches the tracker", st.getServices());
+        
+        st.close();
+    }
+    
+    public void testExistingAspectServiceTracker() {
+        ServiceTracker st = new ServiceTracker(context, ServiceInterface.class.getName(), null);
+        ServiceRegistration sr = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(), null);
+        final long sid = ServiceUtil.getServiceId(sr.getReference());
+        ServiceRegistration asr = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(),
+            new Hashtable() {{ put(DependencyManager.ASPECT, sid); put(Constants.SERVICE_RANKING, 10); }});
+        ServiceRegistration asr2 = context.registerService(ServiceInterface.class.getName(), new ServiceProvider(),
+            new Hashtable() {{ put(DependencyManager.ASPECT, sid); put(Constants.SERVICE_RANKING, 20); }});
+
+        st.open();
+        Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+        Assert.assertEquals("Service ranking should be 20", Integer.valueOf(20), (Integer) st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+        
+        asr2.unregister();
+        Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+        Assert.assertEquals("Service ranking should be 10", Integer.valueOf(10), (Integer) st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+        
+        asr.unregister();
+        Assert.assertEquals("There should be one service that matches the tracker", 1, st.getServices().length);
+        Assert.assertNull("Service should not have a ranking", st.getServiceReference().getProperty(Constants.SERVICE_RANKING));
+        
+        sr.unregister();
+        Assert.assertNull("There should be no service that matches the tracker", st.getServices());
+        
+        st.close();
+    }
+    
+    static interface ServiceInterface {
+        public void invoke();
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        public void invoke() {
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceUpdateTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceUpdateTest.java
new file mode 100644
index 0000000..6bc1522
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceUpdateTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.dm.itest.api;
+
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceUpdateTest extends TestBase {
+    public void testServiceUpdate() throws Exception {
+        final DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a resource provider
+        ResourceProvider provider = new ResourceProvider(context, new URL("file://localhost/path/to/file1.txt"));
+        // activate it
+        m.add(m.createComponent()
+            .setImplementation(new ServiceProvider(e))
+            .add(m.createServiceDependency()
+                .setService(ServiceInterface.class)
+                .setRequired(true)
+                .setCallbacks("add", "change", "remove")
+            )
+        );
+        
+        m.add(m.createComponent()
+            .setImplementation(provider)
+            .add(m.createServiceDependency()
+                .setService(ResourceHandler.class)
+                .setCallbacks("add", "remove")
+            )
+        );
+        
+        // create a resource adapter for our single resource
+        // note that we can provide an actual implementation instance here because there will be only one
+        // adapter, normally you'd want to specify a Class here
+        CallbackInstance callbackInstance = new CallbackInstance(e);
+        Hashtable<String, String> serviceProps = new Hashtable<String, String>();
+        serviceProps.put("number", "1");
+        Component component = m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, callbackInstance, "changed")
+            .setImplementation(new ResourceAdapter(e))
+            .setInterface(ServiceInterface.class.getName(), serviceProps)
+            .setCallbacks(callbackInstance, "init", "start", "stop", "destroy");
+        m.add(component);
+        // wait until the single resource is available
+        e.waitForStep(1, 5000);
+        // wait until the component gets the dependency injected
+        e.waitForStep(2, 5000);
+        // trigger a 'change' in our resource
+        provider.change();
+        // wait until the changed callback is invoked
+        e.waitForStep(3, 5000);        
+        // wait until the changed event arrived at the component
+        e.waitForStep(4, 5000);
+        System.out.println("Done!");
+        m.clear();
+     }
+    
+    static class ResourceAdapter implements ServiceInterface {
+        protected URL m_resource; // injected by reflection.
+        
+        ResourceAdapter(Ensure e) {
+        }
+
+        public void invoke() {
+            // TODO Auto-generated method stub
+            
+        }
+    }
+        
+    static interface ServiceInterface {
+        public void invoke();
+    }
+
+    static class ServiceProvider {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        void add(ServiceInterface i) {
+            m_ensure.step(2);
+        }
+        void change(ServiceInterface i) {
+            System.out.println("Change...");
+            m_ensure.step(4);
+        }
+        void remove(ServiceInterface i) {
+            System.out.println("Remove...");
+        }
+    }    
+    
+    static class CallbackInstance {
+        private final Ensure m_ensure;
+        public CallbackInstance(Ensure e) {
+            m_ensure = e;
+        }
+        
+        void init() {
+            System.out.println("init");
+        }
+        void start() {
+            System.out.println("start");
+            m_ensure.step(1);
+        }
+        void stop() {
+            System.out.println("stop");
+        }
+        void destroy() {
+            System.out.println("destroy");
+        }
+        void changed(Component component) {
+            System.out.println("resource changed");
+            m_ensure.step(3);
+            
+            Properties newProps = new Properties();
+            // update the component's service properties
+            Dictionary<String, String> dict = component.getServiceProperties();
+            Enumeration<String> e = dict.keys();
+            while (e.hasMoreElements()) {
+                String key = e.nextElement();
+                String value = dict.get(key);
+                newProps.setProperty(key, value);
+            }
+            newProps.setProperty("new-property", "2");
+            component.getServiceRegistration().setProperties(newProps);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/TemporalServiceDependencyTest.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/TemporalServiceDependencyTest.java
new file mode 100644
index 0000000..97b1565
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/TemporalServiceDependencyTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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.dm.itest.api;
+
+import java.util.Hashtable;
+
+import org.junit.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "serial"})
+public class TemporalServiceDependencyTest extends TestBase {
+    public void testServiceConsumptionAndIntermittentAvailability() {
+        final DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        TemporalServiceProvider provider = new TemporalServiceProvider(e);
+        Component sp = m.createComponent().setImplementation(provider).setInterface(TemporalServiceInterface.class.getName(), null);
+        TemporalServiceProvider2 provider2 = new TemporalServiceProvider2(e);
+        Component sp2 = m.createComponent().setImplementation(provider2).setInterface(TemporalServiceInterface.class.getName(), null);
+        TemporalServiceConsumer consumer = new TemporalServiceConsumer(e);
+        Component sc = m.createComponent().setImplementation(consumer)
+            .add(m.createTemporalServiceDependency(10000).setService(TemporalServiceInterface.class).setRequired(true));
+        // add the service consumer
+        m.add(sc);
+        // now add the first provider
+        m.add(sp);
+        e.waitForStep(2, 5000);
+        // and remove it again (this should not affect the consumer yet)
+        m.remove(sp);
+        // now add the second provider
+        m.add(sp2);
+        e.step(3);
+        e.waitForStep(4, 5000);
+        // and remove it again
+        m.remove(sp2);
+        // finally remove the consumer
+        m.remove(sc);
+        // ensure we executed all steps inside the component instance
+        e.step(6);
+        m.clear();
+    }
+
+    public void testServiceConsumptionWithCallbackAndIntermittentAvailability() {
+        final DependencyManager m = getDM();
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        TemporalServiceProvider provider = new TemporalServiceProvider(e);
+        Component sp = m.createComponent().setImplementation(provider).setInterface(TemporalServiceInterface.class.getName(), null);
+        TemporalServiceProvider2 provider2 = new TemporalServiceProvider2(e);
+        Component sp2 = m.createComponent().setImplementation(provider2).setInterface(TemporalServiceInterface.class.getName(), null);
+        TemporalServiceConsumerWithCallback consumer = new TemporalServiceConsumerWithCallback(e);
+        ServiceDependency temporalDep =  m.createTemporalServiceDependency(10000).setService(TemporalServiceInterface.class).setRequired(true).setCallbacks("add", "remove");
+        Component sc = m.createComponent().setImplementation(consumer).add(temporalDep);
+            
+        // add the service consumer
+        m.add(sc);
+        // now add the first provider
+        m.add(sp);
+        e.waitForStep(2, 5000);
+        // and remove it again (this should not affect the consumer yet)
+        m.remove(sp);
+        // now add the second provider
+        m.add(sp2);
+        e.step(3);
+        e.waitForStep(4, 5000);
+        // and remove it again
+        m.remove(sp2);
+        // finally remove the consumer
+        m.remove(sc);
+        // Wait for the consumer.remove callback
+        e.waitForStep(6, 5000);
+        // ensure we executed all steps inside the component instance
+        e.step(7);
+        m.clear();
+    }
+
+    public void testFelix4602_PropagateServiceInvocationException() {
+        final DependencyManager m = getDM();
+        final Ensure ensure = new Ensure();
+        Runnable provider = new Runnable() {
+        	public void run() {
+        		throw new UncheckedException();
+        	}
+        };
+        Hashtable props = new Hashtable();
+        props.put("target", getClass().getSimpleName());
+        Component providerComp = m.createComponent()
+        		.setInterface(Runnable.class.getName(), props)
+        		.setImplementation(provider);
+
+        Object consumer = new Object() {
+        	volatile Runnable m_provider;
+        	@SuppressWarnings("unused")
+            void start() {
+        		try {
+        			ensure.step(1);
+        			m_provider.run();
+        		} catch (UncheckedException e) {
+        			ensure.step(2);
+        		}
+        	}
+        };
+        Component consumerComp = m.createComponent()
+        		.setImplementation(consumer)
+        		.add(m.createTemporalServiceDependency(5000)
+        				.setService(Runnable.class, "(target=" + getClass().getSimpleName() + ")")
+        				.setRequired(true));
+        m.add(consumerComp);
+        m.add(providerComp);
+        ensure.waitForStep(2, 5000);
+        m.clear();
+    }
+    
+    static class UncheckedException extends RuntimeException {    	
+    }
+
+    static interface TemporalServiceInterface {
+        public void invoke();
+    }
+
+    static class TemporalServiceProvider implements TemporalServiceInterface {
+        private final Ensure m_ensure;
+        public TemporalServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke() {
+            m_ensure.step(2);
+        }
+    }
+
+    static class TemporalServiceProvider2 implements TemporalServiceInterface {
+        protected final Ensure m_ensure;
+        public TemporalServiceProvider2(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke() {
+            m_ensure.step(4);
+        }
+    }
+
+    static class TemporalServiceConsumer implements Runnable {
+        protected volatile TemporalServiceInterface m_service;
+        protected final Ensure m_ensure;
+
+        public TemporalServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            m_ensure.step(1);
+            Thread t = new Thread(this);
+            t.start();
+        }
+        
+        public void run() {
+            m_service.invoke();
+            m_ensure.waitForStep(3, 15000);
+            m_service.invoke();
+        }
+        
+        public void destroy() {
+            m_ensure.step(5);
+        }
+    }
+    
+    static class TemporalServiceConsumerWithCallback extends TemporalServiceConsumer {
+        public TemporalServiceConsumerWithCallback(Ensure e) {
+            super(e);
+        }
+        
+        public void add(TemporalServiceInterface service) {
+            m_service = service;
+        }
+        
+        public void remove(TemporalServiceInterface service) {
+            Assert.assertTrue(m_service == service);
+            m_ensure.step(6);
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/Activator.java
new file mode 100644
index 0000000..a550e30
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/Activator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.dm.itest.bundle;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Activator extends DependencyActivatorBase {
+
+	@Override
+	public void init(BundleContext ctx, DependencyManager m)
+			throws Exception {
+		m.add(createComponent()
+				.setImplementation(TestComponent.class)
+				.setInterface(TestService.class.getName(), null));
+	}
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/TestComponent.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/TestComponent.java
new file mode 100644
index 0000000..2ff626f
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/TestComponent.java
@@ -0,0 +1,28 @@
+/*
+ * 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.dm.itest.bundle;
+
+/**
+ * Test Component used by the FELIX2955_ShellCommandTest test.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class TestComponent implements TestService {
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/TestService.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/TestService.java
new file mode 100644
index 0000000..6393075
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/bundle/TestService.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dm.itest.bundle;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface TestService {
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/Ensure.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/Ensure.java
new file mode 100644
index 0000000..9c34741
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/Ensure.java
@@ -0,0 +1,174 @@
+/*
+ * 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.dm.itest.util;
+
+import java.io.PrintStream;
+
+import org.junit.Assert;
+
+/**
+ * Helper class to make sure that steps in a test happen in the correct order. Instantiate
+ * this class and subsequently invoke <code>step(nr)</code> with steps starting at 1. You
+ * can also have threads wait until you arrive at a certain step.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Ensure {
+    private final boolean DEBUG;
+    private static long INSTANCE = 0;
+    private static final int RESOLUTION = 100;
+    private static PrintStream STREAM = System.out;
+    int step = 0;
+    private Throwable m_throwable;
+    
+    public Ensure() {
+        this(true);
+    }
+    
+    public Ensure(boolean debug) {
+        DEBUG = debug;
+        if (DEBUG) {
+            INSTANCE++;
+        }
+    }
+
+    public void setStream(PrintStream output) {
+        STREAM = output;
+    }
+    
+    /**
+     * Mark this point as step <code>nr</code>.
+     * 
+     * @param nr the step we are in
+     */
+    public synchronized void step(int nr) {
+        step++;
+        Assert.assertEquals(nr, step);
+        if (DEBUG) {
+            String info = getLineInfo(3);
+            STREAM.println("[Ensure " + INSTANCE + "] step " + step + " [" + currentThread() + "] " + info);
+        }
+        notifyAll();
+    }
+
+    private String getLineInfo(int depth) {
+        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
+        String info = trace[depth].getClassName() + "." + trace[depth].getMethodName() + ":" + trace[depth].getLineNumber();
+        return info;
+    }
+    
+    /**
+     * Mark this point as the next step.
+     */
+    public synchronized void step() {
+        step++;
+        if (DEBUG) {
+            String info = getLineInfo(3);
+            STREAM.println("[Ensure " + INSTANCE + "] next step " + step + " [" + currentThread() + "] " + info);
+        }
+        notifyAll();
+    }
+
+    /**
+     * Wait until we arrive at least at step <code>nr</code> in the process, or fail if that
+     * takes more than <code>timeout</code> milliseconds. If you invoke wait on a thread,
+     * you are effectively assuming some other thread will invoke the <code>step(nr)</code>
+     * method.
+     * 
+     * @param nr the step to wait for
+     * @param timeout the number of milliseconds to wait
+     */
+    public synchronized void waitForStep(int nr, int timeout) {
+        final int initialTimeout = timeout;
+        if (DEBUG) {
+            String info = getLineInfo(3);
+            STREAM.println("[Ensure " + INSTANCE + "] waiting for step " + nr + " [" + currentThread() + "] " + info);
+        }
+        while (step < nr && timeout > 0) {
+            try {
+                wait(RESOLUTION);
+                timeout -= RESOLUTION;
+            }
+            catch (InterruptedException e) {}
+        }
+        if (step < nr) {
+            throw new IllegalStateException("Timed out waiting for " + initialTimeout + " ms for step " + nr + ", we are still at step " + step);
+        }
+        if (DEBUG) {
+            String info = getLineInfo(3);
+            STREAM.println("[Ensure " + INSTANCE + "] arrived at step " + nr + " [" + currentThread() + "] " + info);
+        }
+    }
+    
+    private String currentThread() {
+        Thread thread = Thread.currentThread();
+        return thread.getId() + " " + thread.getName();
+    }
+    
+    public static Runnable createRunnableStep(final Ensure ensure, final int nr) {
+        return new Runnable() { public void run() { ensure.step(nr); }};
+    }
+    
+    public synchronized void steps(Steps steps) {
+        steps.next(this);
+    }
+    
+    /** 
+     * Helper class for naming a list of step numbers. If used with the steps(Steps) method
+     * you can define at which steps in time this point should be passed. That means you can
+     * check methods that will get invoked multiple times during a test.
+     */
+    public static class Steps {
+        private final int[] m_steps;
+        private int m_stepIndex;
+
+        /** 
+         * Create a list of steps and initialize the step counter to zero.
+         */
+        public Steps(int... steps) {
+            m_steps = steps;
+            m_stepIndex = 0;
+        }
+
+        /**
+         * Ensure we're at the right step. Will throw an index out of bounds exception if we enter this step more often than defined.
+         */
+        public void next(Ensure ensure) {
+            ensure.step(m_steps[m_stepIndex++]);
+        }
+    }
+
+    /**
+     * Saves a thrown exception that occurred in a different thread. You can only save one exception
+     * at a time this way.
+     */
+    public synchronized void throwable(Throwable throwable) {
+        m_throwable = throwable;
+    }
+
+    /**
+     * Throws a <code>Throwable</code> if one occurred in a different thread and that thread saved it
+     * using the <code>throwable()</code> method.
+     */
+    public synchronized void ensure() throws Throwable {
+        if (m_throwable != null) {
+            throw m_throwable;
+        }
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/ServiceUtil.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/ServiceUtil.java
new file mode 100644
index 0000000..628189c
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/ServiceUtil.java
@@ -0,0 +1,176 @@
+/*
+ * 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.dm.itest.util;
+
+import java.util.List;
+
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * OSGi service utilities (copied from dependency manager implementation).
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceUtil {
+    /**
+     * Returns the service ranking of a service, based on its service reference. If
+     * the service has a property specifying its ranking, that will be returned. If
+     * not, the default ranking of zero will be returned.
+     * 
+     * @param ref the service reference to determine the ranking for
+     * @return the ranking
+     */
+    public static int getRanking(ServiceReference ref) {
+        return getRankingAsInteger(ref).intValue();
+    }
+    
+    /**
+     * Returns the service ranking of a service, based on its service reference. If
+     * the service has a property specifying its ranking, that will be returned. If
+     * not, the default ranking of zero will be returned.
+     * 
+     * @param ref the service reference to determine the ranking for
+     * @return the ranking
+     */
+    public static Integer getRankingAsInteger(ServiceReference ref) {
+        Integer rank = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
+        if (rank != null) {
+            return rank;
+        }
+        return new Integer(0);
+    }
+    
+    /**
+     * Returns the service ID of a service, based on its service reference. This
+     * method is aware of service aspects as defined by the dependency manager and
+     * will return the ID of the orginal service if you give it an aspect.
+     * 
+     * @param ref the service reference to determine the service ID of
+     * @return the service ID
+     */
+    public static long getServiceId(ServiceReference ref) {
+        return getServiceIdAsLong(ref).longValue();
+    }
+    
+    /**
+     * Returns the service ID of a service, based on its service reference. This
+     * method is aware of service aspects as defined by the dependency manager and
+     * will return the ID of the orginal service if you give it an aspect.
+     * 
+     * @param ref the service reference to determine the service ID of
+     * @return the service ID
+     */
+    public static Long getServiceIdAsLong(ServiceReference ref) {
+    	return getServiceIdObject(ref);
+    }
+    
+    public static Long getServiceIdObject(ServiceReference ref) {
+        Long aid = (Long) ref.getProperty(DependencyManager.ASPECT);
+        if (aid != null) {
+            return aid;
+        }
+        Long sid = (Long) ref.getProperty(Constants.SERVICE_ID);
+        if (sid != null) {
+            return sid;
+        }
+        throw new IllegalArgumentException("Invalid service reference, no service ID found");
+    }
+
+    /**
+     * Determines if the service is an aspect as defined by the dependency manager.
+     * Aspects are defined by a property and this method will check for its presence.
+     * 
+     * @param ref the service reference
+     * @return <code>true</code> if it's an aspect, <code>false</code> otherwise
+     */
+    public static boolean isAspect(ServiceReference ref) {
+        Long aid = (Long) ref.getProperty(DependencyManager.ASPECT);
+        return (aid != null);
+    }
+    
+    /**
+     * Converts a service reference to a string, listing both the bundle it was
+     * registered from and all properties.
+     * 
+     * @param ref the service reference
+     * @return a string representation of the service
+     */
+    public static String toString(ServiceReference ref) {
+        if (ref == null) {
+            return "ServiceReference[null]";
+        }
+        else {
+            StringBuffer buf = new StringBuffer();
+            Bundle bundle = ref.getBundle();
+            if (bundle != null) {
+                buf.append("ServiceReference[");
+                buf.append(bundle.getBundleId());
+                buf.append("]{");
+            }
+            else {
+                buf.append("ServiceReference[unregistered]{");
+            }
+            buf.append(propertiesToString(ref, null));
+            buf.append("}");
+            return buf.toString();
+        }
+    }
+    
+    /**
+     * Converts the properties of a service reference to a string.
+     * 
+     * @param ref the service reference
+     * @param exclude a list of properties to exclude, or <code>null</code> to show everything
+     * @return a string representation of the service properties
+     */
+    public static String propertiesToString(ServiceReference ref, List<String> exclude) {
+        StringBuffer buf = new StringBuffer();
+        String[] keys = ref.getPropertyKeys();
+        for (int i = 0; i < keys.length; i++) {
+            if (i > 0) { 
+                buf.append(','); 
+            }
+            buf.append(keys[i]);
+            buf.append('=');
+            Object val = ref.getProperty(keys[i]);
+            if (exclude == null || !exclude.contains(val)) {
+                if (val instanceof String[]) {
+                    String[] valArray = (String[]) val;
+                    StringBuffer valBuf = new StringBuffer();
+                    valBuf.append('{');
+                    for (int j = 0; j < valArray.length; j++) {
+                        if (valBuf.length() > 1) {
+                            valBuf.append(',');
+                        }
+                        valBuf.append(valArray[j].toString());
+                    }
+                    valBuf.append('}');
+                    buf.append(valBuf);
+                }
+                else {
+                    buf.append(val.toString());
+                }
+            }
+        }
+        return buf.toString();
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/TestBase.java b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/TestBase.java
new file mode 100644
index 0000000..4cedfb6
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/TestBase.java
@@ -0,0 +1,354 @@
+/*
+ * 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.dm.itest.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Hashtable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import junit.framework.TestCase;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentExecutorFactory;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogService;
+
+/**
+ * Base class for all integration tests.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class TestBase extends TestCase implements LogService, FrameworkListener {
+    // Default OSGI log service level.
+    protected final static int LOG_LEVEL = LogService.LOG_WARNING;
+    
+    // optional thread pool used by parallel dependency managers
+    private volatile ExecutorService m_threadPool;
+    
+    // flag used to check if the threadpool must be used for a given test.
+    protected volatile boolean m_parallel;
+        
+    // Flag used to check if some errors have been logged during the execution of a given test.
+    private volatile boolean m_errorsLogged;
+
+    // We implement OSGI log service.
+    protected ServiceRegistration logService;
+    
+    // Our bundle context
+    protected BundleContext context;
+
+    // Our dependency manager used to create test components.
+    protected volatile DependencyManager m_dm;
+
+    // The Registration for the DM threadpool.
+    private ServiceRegistration m_componentExecutorFactoryReg;
+
+    public TestBase() {
+    }
+       
+    protected void setParallel() {
+        m_parallel = true;
+    }
+    
+    public void setUp() throws Exception {
+    	warn("Setting up test " + getClass().getName());
+    	context = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
+    	Hashtable<String, Object> props = new Hashtable<>();
+    	props.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE));
+        logService = context.registerService(LogService.class.getName(), this, props);
+        context.addFrameworkListener(this);
+        m_dm = new DependencyManager(context);
+        if (m_parallel) {
+            warn("Using threadpool ...");
+            m_threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+            m_componentExecutorFactoryReg = context.registerService(ComponentExecutorFactory.class.getName(), 
+                new ComponentExecutorFactory() {
+                    @Override
+                    public Executor getExecutorFor(Component component) {
+                        return m_threadPool;
+                    }
+                },
+                null);
+        }
+    }
+    
+    public void tearDown() throws Exception {
+    	warn("Tearing down test " + getClass().getName());
+    	logService.unregister();
+    	context.removeFrameworkListener(this);
+        clearComponents();
+        if (m_parallel && m_componentExecutorFactoryReg != null) {
+    	    m_componentExecutorFactoryReg.unregister();
+    	    m_threadPool.shutdown();
+            try {
+                m_threadPool.awaitTermination(60, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+            }
+    	}
+        Assert.assertFalse(errorsLogged());
+    }
+        
+    protected DependencyManager getDM() {
+        return m_dm;
+    }
+                
+    protected void clearComponents() throws InterruptedException {
+        m_dm.clear();
+        warn("All component cleared.");
+    }
+
+    /**
+     * Creates and provides an Ensure object with a name service property into the OSGi service registry.
+     */
+    protected ServiceRegistration register(Ensure e, String name) {
+        Hashtable<String, String> props = new Hashtable<String, String>();
+        props.put("name", name);
+        return context.registerService(Ensure.class.getName(), e, props);
+    }
+
+    /**
+     * Helper method used to stop a given bundle.
+     * 
+     * @param symbolicName
+     *            the symbolic name of the bundle to be stopped.
+     */
+    protected void stopBundle(String symbolicName) {
+        // Stop the test.annotation bundle
+        boolean found = false;
+        for (Bundle b : context.getBundles()) {
+            if (b.getSymbolicName().equals(symbolicName)) {
+                try {
+                    found = true;
+                    b.stop();
+                } catch (BundleException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        if (!found) {
+            throw new IllegalStateException("bundle " + symbolicName + " not found");
+        }
+    }
+
+    /**
+     * Helper method used to start a given bundle.
+     * 
+     * @param symbolicName
+     *            the symbolic name of the bundle to be started.
+     */
+    protected void startBundle(String symbolicName) {
+        // Stop the test.annotation bundle
+        boolean found = false;
+        for (Bundle b : context.getBundles()) {
+            if (b.getSymbolicName().equals(symbolicName)) {
+                try {
+                    found = true;
+                    b.start();
+                } catch (BundleException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        if (!found) {
+            throw new IllegalStateException("bundle " + symbolicName + " not found");
+        }
+    }
+
+    /**
+     * Helper method used to get a given bundle.
+     * 
+     * @param symbolicName
+     *            the symbolic name of the bundle to get.
+     */
+    protected Bundle getBundle(String symbolicName) {
+        for (Bundle b : context.getBundles()) {
+            if (b.getSymbolicName().equals(symbolicName)) {
+                return b;
+            }
+        }
+        throw new IllegalStateException("bundle " + symbolicName + " not found");
+    }
+    
+    /**
+     * Suspend the current thread for a while.
+     * 
+     * @param n
+     *            the number of milliseconds to wait for.
+     */
+    protected void sleep(int ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    public void log(int level, String message) {
+        checkError(level, null);
+        if (LOG_LEVEL >= level) {
+            System.out.println(getLevel(level) + " - " + Thread.currentThread().getName() + " : " + message);
+        }
+    }
+
+    public void log(int level, String message, Throwable exception) {
+        checkError(level, exception);
+        if (LOG_LEVEL >= level) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(getLevel(level) + " - " + Thread.currentThread().getName() + " : ");
+            sb.append(message);
+            parse(sb, exception);
+            System.out.println(sb.toString());
+        }
+    }
+
+    public void log(ServiceReference sr, int level, String message) {
+        checkError(level, null);
+        if (LOG_LEVEL >= level) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(getLevel(level) + " - " + Thread.currentThread().getName() + " : ");
+            sb.append(message);
+            System.out.println(sb.toString());
+        }
+    }
+
+    public void log(ServiceReference sr, int level, String message, Throwable exception) {
+        checkError(level, exception);
+        if (LOG_LEVEL >= level) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(getLevel(level) + " - " + Thread.currentThread().getName() + " : ");
+            sb.append(message);
+            parse(sb, exception);
+            System.out.println(sb.toString());
+        }
+    }
+
+    protected boolean errorsLogged() {
+        return m_errorsLogged;
+    }
+
+    private void parse(StringBuilder sb, Throwable t) {
+        if (t != null) {
+            sb.append(" - ");
+            StringWriter buffer = new StringWriter();
+            PrintWriter pw = new PrintWriter(buffer);
+            t.printStackTrace(pw);
+            sb.append(buffer.toString());
+            m_errorsLogged = true;
+        }
+    }
+
+    private String getLevel(int level) {
+        switch (level) {
+            case LogService.LOG_DEBUG :
+                return "DEBUG";
+            case LogService.LOG_ERROR :
+                return "ERROR";
+            case LogService.LOG_INFO :
+                return "INFO";
+            case LogService.LOG_WARNING :
+                return "WARN";
+            default :
+                return "";
+        }
+    }
+
+    private void checkError(int level, Throwable exception) {
+        if (level <= LOG_ERROR) {
+            m_errorsLogged = true;
+        }
+        if (exception != null) {
+            m_errorsLogged = true;
+        }
+    }
+
+    public void frameworkEvent(FrameworkEvent event) {
+        int eventType = event.getType();
+        String msg = getFrameworkEventMessage(eventType);
+        int level = (eventType == FrameworkEvent.ERROR) ? LOG_ERROR : LOG_WARNING;
+        if (msg != null) {
+            log(level, msg, event.getThrowable());
+        } else {
+            log(level, "Unknown fwk event: " + event);
+        }
+    }
+
+    private String getFrameworkEventMessage(int event) {
+        switch (event) {
+            case FrameworkEvent.ERROR :
+                return "FrameworkEvent: ERROR";
+            case FrameworkEvent.INFO :
+                return "FrameworkEvent INFO";
+            case FrameworkEvent.PACKAGES_REFRESHED :
+                return "FrameworkEvent: PACKAGE REFRESHED";
+            case FrameworkEvent.STARTED :
+                return "FrameworkEvent: STARTED";
+            case FrameworkEvent.STARTLEVEL_CHANGED :
+                return "FrameworkEvent: STARTLEVEL CHANGED";
+            case FrameworkEvent.WARNING :
+                return "FrameworkEvent: WARNING";
+            default :
+                return null;
+        }
+    }
+
+    protected void warn(String msg, Object ... params) {
+	if (LOG_LEVEL >= LogService.LOG_WARNING) {
+	    log(LogService.LOG_WARNING, params.length > 0 ? String.format(msg, params) : msg);
+	}
+    }
+
+    @SuppressWarnings("unused")
+    protected void info(String msg, Object ... params) {
+	if (LOG_LEVEL >= LogService.LOG_INFO) {
+	    log(LogService.LOG_INFO, params.length > 0 ? String.format(msg, params) : msg);
+	}
+    }
+
+    @SuppressWarnings("unused")
+    protected void debug(String msg, Object ... params) {
+	if (LOG_LEVEL >= LogService.LOG_DEBUG) {
+	    log(LogService.LOG_DEBUG, params.length > 0 ? String.format(msg, params) : msg);
+	}
+    }
+
+    protected void error(String msg, Object ... params) {
+        log(LogService.LOG_ERROR, params.length > 0 ? String.format(msg, params) : msg);
+    }
+
+    protected void error(String msg, Throwable err, Object ... params) {
+        log(LogService.LOG_ERROR, params.length > 0 ? String.format(msg, params) : msg, err);
+    }
+
+    protected void error(Throwable err) {
+        log(LogService.LOG_ERROR, "error", err);
+    }
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/packageinfo b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/packageinfo
new file mode 100644
index 0000000..e252556
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/util/packageinfo
@@ -0,0 +1 @@
+version 1.0.0
\ No newline at end of file