You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pd...@apache.org on 2014/03/18 22:47:17 UTC

svn commit: r1579067 - /felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/

Author: pderop
Date: Tue Mar 18 21:47:17 2014
New Revision: 1579067

URL: http://svn.apache.org/r1579067
Log:
added bundle dependency, bundle adapter, resource dependency, resource adapter, timed service dependency tests

Added:
    felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/BundleAdapterTest.java
    felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/BundleDependencyTest.java
    felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FELIX2348_ResourceAdapterTest.java
    felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FactoryConfigurationAdapterTest.java
    felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FilterIndexResourceAdapterTest.java
    felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ModifiedBundleDependencyTest.java
    felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterDependencyAddAndRemoveTest.java
    felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterDependencyAddAndRemoveTest2.java
    felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterTest.java
    felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceDependencyTest.java
    felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ServiceUpdateTest.java
    felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/TemporalServiceDependencyTest.java

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/BundleAdapterTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/BundleAdapterTest.java?rev=1579067&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/BundleAdapterTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/BundleAdapterTest.java Tue Mar 18 21:47:17 2014
@@ -0,0 +1,72 @@
+package dm.it;
+
+import junit.framework.Assert;
+
+import org.osgi.framework.Bundle;
+
+import dm.Component;
+import dm.DependencyManager;
+
+// TODO add more tests
+public class BundleAdapterTest extends TestBase {
+    private final static String BSN = "org.apache.felix.metatype";
+    
+    public void testBundleAdapter() {
+        DependencyManager m = new DependencyManager(context);
+        // 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);
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/BundleDependencyTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/BundleDependencyTest.java?rev=1579067&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/BundleDependencyTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/BundleDependencyTest.java Tue Mar 18 21:47:17 2014
@@ -0,0 +1,121 @@
+package dm.it;
+
+import junit.framework.Assert;
+
+import org.osgi.framework.Bundle;
+
+import dm.Component;
+import dm.DependencyManager;
+
+public class BundleDependencyTest extends TestBase {
+    private final static String BSN = "org.apache.felix.metatype";
+    
+    public void testBundleDependencies() {
+        DependencyManager m = new DependencyManager(context);
+        // 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 = new DependencyManager(context);
+        // create a service provider and consumer
+        Consumer c = new Consumer();
+        
+        // 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);
+            }
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FELIX2348_ResourceAdapterTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FELIX2348_ResourceAdapterTest.java?rev=1579067&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FELIX2348_ResourceAdapterTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FELIX2348_ResourceAdapterTest.java Tue Mar 18 21:47:17 2014
@@ -0,0 +1,122 @@
+package dm.it;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import junit.framework.Assert;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+import dm.DependencyManager;
+import dm.ResourceHandler;
+import dm.ResourceUtil;
+
+public class FELIX2348_ResourceAdapterTest extends TestBase {
+    public void testBasicResourceAdapter() throws Exception {
+        DependencyManager m = new DependencyManager(context);
+        // 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)));
+        m.add(m.createComponent().setImplementation(new ResourceProvider(e)).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 {
+                InputStream in = m_resource.openStream();
+            } 
+            catch (FileNotFoundException e) {
+                m_ensure.step(3);
+            }
+            catch (IOException e) {
+                Assert.fail("We should not have gotten this exception.");
+            }
+        }
+    }
+    
+    static class ResourceProvider {
+        private volatile BundleContext m_context;
+        private final Ensure m_ensure;
+        private final Map m_handlers = new HashMap();
+        private URL[] m_resources;
+
+        public ResourceProvider(Ensure ensure) throws MalformedURLException {
+            m_ensure = ensure;
+            m_resources = new URL[] {
+                new URL("file://localhost/path/to/file1.txt")
+            };
+        }
+        
+        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;
+                }
+            }
+            synchronized (m_handlers) {
+                m_handlers.put(handler, filter);
+            }
+            for (int i = 0; i < m_resources.length; i++) {
+                if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+                    handler.added(m_resources[i]);
+                }
+            }
+        }
+
+        public void remove(ServiceReference ref, ResourceHandler handler) {
+            Filter filter;
+            synchronized (m_handlers) {
+                filter = (Filter) m_handlers.remove(handler);
+            }
+            removeResources(handler, filter);
+        }
+
+        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() {
+            Entry[] handlers;
+            synchronized (m_handlers) {
+                handlers = (Entry[]) m_handlers.entrySet().toArray(new Entry[m_handlers.size()]);
+            }
+            for (int i = 0; i < handlers.length; i++) {
+                removeResources((ResourceHandler) handlers[i].getKey(), (Filter) handlers[i].getValue());
+            }
+            
+            System.out.println("DESTROY..." + m_handlers.size());
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FactoryConfigurationAdapterTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FactoryConfigurationAdapterTest.java?rev=1579067&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FactoryConfigurationAdapterTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FactoryConfigurationAdapterTest.java Tue Mar 18 21:47:17 2014
@@ -0,0 +1,202 @@
+package dm.it;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Properties;
+
+import junit.framework.Assert;
+
+import org.osgi.service.cm.ConfigurationAdmin;
+
+import dm.Component;
+import dm.DependencyManager;
+
+public class FactoryConfigurationAdapterTest extends TestBase
+{
+    private static Ensure m_ensure;
+    
+    public void testFactoryConfigurationAdapter() {
+        DependencyManager m = new DependencyManager(context);
+        // 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.createAdapterFactoryConfigurationService("MyFactoryPid", "updated", true /* propagate CM settings */)
+                      .setInterface(AdapterService.class.getName(), new Properties() {{ 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);
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FilterIndexResourceAdapterTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FilterIndexResourceAdapterTest.java?rev=1579067&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FilterIndexResourceAdapterTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/FilterIndexResourceAdapterTest.java Tue Mar 18 21:47:17 2014
@@ -0,0 +1,155 @@
+package dm.it;
+
+//import static org.ops4j.pax.exam.CoreOptions.waitForFrameworkStartupFor;
+//import static org.ops4j.pax.exam.container.def.PaxRunnerOptions.vmOption;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import junit.framework.Assert;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+import dm.DependencyManager;
+import dm.ResourceHandler;
+import dm.ResourceUtil;
+
+public class FilterIndexResourceAdapterTest extends TestBase {
+  public void testBasicResourceAdapter() throws Exception {
+      System.setProperty("org.apache.felix.dependencymanager.filterindex", "objectClass");
+      DependencyManager m = new DependencyManager(context);
+      // helper class that ensures certain steps get executed in sequence
+      Ensure e = new Ensure();
+      // create a resource provider
+      ResourceProvider provider = new ResourceProvider(e);
+      // 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 {
+              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);
+      }
+  }
+  
+  static class ResourceProvider {
+      private volatile BundleContext m_context;
+      private final Ensure m_ensure;
+      private final Map m_handlers = new HashMap();
+      private URL[] m_resources;
+
+      public ResourceProvider(Ensure ensure) throws MalformedURLException {
+          m_ensure = ensure;
+          m_resources = new URL[] {
+              new URL("file://localhost/path/to/file1.txt")
+          };
+      }
+      
+      public void change() {
+          ResourceHandler[] handlers;
+          synchronized (m_handlers) {
+              handlers = (ResourceHandler[]) m_handlers.keySet().toArray(new ResourceHandler[m_handlers.size()]);
+          }
+          for (int i = 0; i < m_resources.length; i++) {
+              for (int j = 0; j < handlers.length; j++) {
+                  ResourceHandler handler = handlers[j];
+                  handler.changed(m_resources[i]);
+              }
+          }
+      }
+
+      public void add(ServiceReference ref, ResourceHandler handler) {
+          System.out.println("RP.add: " + ServiceUtil.toString(ref));
+          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;
+              }
+          }
+          synchronized (m_handlers) {
+              m_handlers.put(handler, filter);
+          }
+          for (int i = 0; i < m_resources.length; i++) {
+              if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+                  handler.added(m_resources[i]);
+              }
+          }
+      }
+
+      public void remove(ServiceReference ref, ResourceHandler handler) {
+          Filter filter;
+          synchronized (m_handlers) {
+              filter = (Filter) m_handlers.remove(handler);
+          }
+          removeResources(handler, filter);
+      }
+
+      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() {
+          Entry[] handlers;
+          synchronized (m_handlers) {
+              handlers = (Entry[]) m_handlers.entrySet().toArray(new Entry[m_handlers.size()]);
+          }
+          for (int i = 0; i < handlers.length; i++) {
+              removeResources((ResourceHandler) handlers[i].getKey(), (Filter) handlers[i].getValue());
+          }
+          
+          System.out.println("DESTROY..." + m_handlers.size());
+      }
+  }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ModifiedBundleDependencyTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ModifiedBundleDependencyTest.java?rev=1579067&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ModifiedBundleDependencyTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ModifiedBundleDependencyTest.java Tue Mar 18 21:47:17 2014
@@ -0,0 +1,121 @@
+package dm.it;
+
+import junit.framework.Assert;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+import dm.Component;
+import dm.DependencyManager;
+
+/**
+ * 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 = new DependencyManager(context);
+        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);                
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterDependencyAddAndRemoveTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterDependencyAddAndRemoveTest.java?rev=1579067&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterDependencyAddAndRemoveTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterDependencyAddAndRemoveTest.java Tue Mar 18 21:47:17 2014
@@ -0,0 +1,245 @@
+package dm.it;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import junit.framework.Assert;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+import dm.Component;
+import dm.ComponentState;
+import dm.ComponentStateListener;
+import dm.Dependency;
+import dm.DependencyManager;
+import dm.ResourceHandler;
+import dm.ResourceUtil;
+
+public class ResourceAdapterDependencyAddAndRemoveTest extends TestBase {
+    public void testBasicResourceAdapter() throws Exception {
+        DependencyManager m = new DependencyManager(context);
+        // 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(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
+        // 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, d))
+            .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.
+        private Ensure m_ensure;
+        private final Dependency m_dependency;
+        
+        ResourceAdapter(Ensure e, Dependency d) {
+            m_ensure = e;
+            m_dependency = d;
+        }
+        
+        void init(Component c) {
+            c.add(m_dependency);
+        }        
+    }
+    
+    class ResourceProvider {
+        private volatile BundleContext m_context;
+        private final Ensure m_ensure;
+        private final Map m_handlers = new HashMap();
+        private URL[] m_resources;
+
+        public ResourceProvider(Ensure ensure) throws MalformedURLException {
+            m_ensure = ensure;
+            m_resources = new URL[] {
+                new URL("file://localhost/path/to/file1.txt")
+            };
+        }
+        
+        public void change() {
+            ResourceHandler[] handlers;
+            synchronized (m_handlers) {
+                handlers = (ResourceHandler[]) m_handlers.keySet().toArray(new ResourceHandler[m_handlers.size()]);
+            }
+            for (int i = 0; i < m_resources.length; i++) {
+                for (int j = 0; j < handlers.length; j++) {
+                    ResourceHandler handler = handlers[j];
+                    handler.changed(m_resources[i]);
+                }
+            }
+        }
+
+        public void add(ServiceReference ref, ResourceHandler handler) {
+            debug("ResourceProvider.add(ref=%s, handler=%s", ref, 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;
+                }
+            }
+            synchronized (m_handlers) {
+                m_handlers.put(handler, filter);
+            }
+            for (int i = 0; i < m_resources.length; i++) {
+                if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+                    handler.added(m_resources[i]);
+                }
+            }
+        }
+
+        public void remove(ServiceReference ref, ResourceHandler handler) {
+            Filter filter;
+            synchronized (m_handlers) {
+                debug("ResourceProvider.remove: ref=%s, handler=%s, handlers=%s", ref, handler, m_handlers);
+                filter = (Filter) m_handlers.remove(handler);
+            }
+            removeResources(handler, filter);
+        }
+
+        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() {
+            debug("ResourceProvider:%s", m_handlers);
+            Entry[] handlers;
+            synchronized (m_handlers) {
+                debug("ResourceProvider.destroy: handlers=%s", m_handlers);
+                handlers = (Entry[]) m_handlers.entrySet().toArray(new Entry[m_handlers.size()]);
+            }
+            for (int i = 0; i < handlers.length; i++) {
+                removeResources((ResourceHandler) handlers[i].getKey(), (Filter) handlers[i].getValue());
+            }
+            
+            debug("DESTROY...%d", m_handlers.size());
+        }
+    }
+    
+    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() {
+        }
+    }    
+    
+    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() {
+            debug("CallbackInstance.init");
+        }
+        
+        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(ComponentState state) {
+            debug("ComponentStateListenerImpl.changed: state=%s", state);
+            switch (state) {
+            case INACTIVE:
+                System.out.println("stopped");
+                m_ensure.step(4);
+            }
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterDependencyAddAndRemoveTest2.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterDependencyAddAndRemoveTest2.java?rev=1579067&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterDependencyAddAndRemoveTest2.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterDependencyAddAndRemoveTest2.java Tue Mar 18 21:47:17 2014
@@ -0,0 +1,247 @@
+package dm.it;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+import junit.framework.Assert;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+import dm.Component;
+import dm.ComponentState;
+import dm.ComponentStateListener;
+import dm.Dependency;
+import dm.DependencyManager;
+import dm.ResourceHandler;
+import dm.ResourceUtil;
+
+public class ResourceAdapterDependencyAddAndRemoveTest2 extends TestBase {
+    public void testBasicResourceAdapter() throws Exception {
+        DependencyManager m = new DependencyManager(context);
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a resource provider
+        ResourceProvider provider = new ResourceProvider(e);
+        // activate it
+        Properties props = new Properties();
+        props.setProperty("id", "1");
+        m.add(m.createComponent()
+            .setInterface(ServiceInterface.class.getName(), props)
+            .setImplementation(new ServiceProvider(e))
+        );
+        
+        props = new Properties();
+        props.setProperty("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.
+        private Ensure m_ensure;
+        final Dependency m_dependency;
+        
+        ResourceAdapter(Ensure e, Dependency d) {
+            m_ensure = e;
+            m_dependency = d;
+        }
+        
+        void init(Component c) {
+            c.add(m_dependency);
+        }
+    }
+    
+    static class ResourceProvider {
+        private volatile BundleContext m_context;
+        private final Ensure m_ensure;
+        private final Map m_handlers = new HashMap();
+        private URL[] m_resources;
+
+        public ResourceProvider(Ensure ensure) throws MalformedURLException {
+            m_ensure = ensure;
+            m_resources = new URL[] {
+                new URL("file://localhost/path/to/file1.txt")
+            };
+        }
+        
+        public void change() {
+            ResourceHandler[] handlers;
+            synchronized (m_handlers) {
+                handlers = (ResourceHandler[]) m_handlers.keySet().toArray(new ResourceHandler[m_handlers.size()]);
+            }
+            for (int i = 0; i < m_resources.length; i++) {
+                for (int j = 0; j < handlers.length; j++) {
+                    ResourceHandler handler = handlers[j];
+                    handler.changed(m_resources[i]);
+                }
+            }
+        }
+
+        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;
+                }
+            }
+            synchronized (m_handlers) {
+                m_handlers.put(handler, filter);
+            }
+            for (int i = 0; i < m_resources.length; i++) {
+                if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+                    handler.added(m_resources[i]);
+                }
+            }
+        }
+
+        public void remove(ServiceReference ref, ResourceHandler handler) {
+            Filter filter;
+            synchronized (m_handlers) {
+                filter = (Filter) m_handlers.remove(handler);
+            }
+            removeResources(handler, filter);
+        }
+
+        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() {
+            Entry[] handlers;
+            synchronized (m_handlers) {
+                handlers = (Entry[]) m_handlers.entrySet().toArray(new Entry[m_handlers.size()]);
+            }
+            for (int i = 0; i < handlers.length; i++) {
+                removeResources((ResourceHandler) handlers[i].getKey(), (Filter) handlers[i].getValue());
+            }
+        }
+    }
+    
+    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() {
+        }
+    }    
+    
+    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() {
+            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 {
+        private final Ensure m_ensure;
+        
+        public ComponentStateListenerImpl(Ensure e) {
+            this.m_ensure = e;
+        }
+
+        @Override
+        public void changed(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;
+
+            }
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterTest.java?rev=1579067&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceAdapterTest.java Tue Mar 18 21:47:17 2014
@@ -0,0 +1,150 @@
+package dm.it;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import junit.framework.Assert;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+import dm.DependencyManager;
+import dm.ResourceHandler;
+import dm.ResourceUtil;
+
+public class ResourceAdapterTest extends TestBase {
+    public void testBasicResourceAdapter() throws Exception {
+        DependencyManager m = new DependencyManager(context);
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a resource provider
+        ResourceProvider provider = new ResourceProvider(e);
+        // 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 {
+                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);
+        }
+    }
+    
+    static class ResourceProvider {
+        private volatile BundleContext m_context;
+        private final Ensure m_ensure;
+        private final Map m_handlers = new HashMap();
+        private URL[] m_resources;
+
+        public ResourceProvider(Ensure ensure) throws MalformedURLException {
+            m_ensure = ensure;
+            m_resources = new URL[] {
+                new URL("file://localhost/path/to/file1.txt")
+            };
+        }
+        
+        public void change() {
+            ResourceHandler[] handlers;
+            synchronized (m_handlers) {
+                handlers = (ResourceHandler[]) m_handlers.keySet().toArray(new ResourceHandler[m_handlers.size()]);
+            }
+            for (int i = 0; i < m_resources.length; i++) {
+                for (int j = 0; j < handlers.length; j++) {
+                    ResourceHandler handler = handlers[j];
+                    handler.changed(m_resources[i]);
+                }
+            }
+        }
+
+        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;
+                }
+            }
+            synchronized (m_handlers) {
+                m_handlers.put(handler, filter);
+            }
+            for (int i = 0; i < m_resources.length; i++) {
+                if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+                    handler.added(m_resources[i]);
+                }
+            }
+        }
+
+        public void remove(ServiceReference ref, ResourceHandler handler) {
+            Filter filter;
+            synchronized (m_handlers) {
+                filter = (Filter) m_handlers.remove(handler);
+            }
+            removeResources(handler, filter);
+        }
+
+        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() {
+            Entry[] handlers;
+            synchronized (m_handlers) {
+                handlers = (Entry[]) m_handlers.entrySet().toArray(new Entry[m_handlers.size()]);
+            }
+            for (int i = 0; i < handlers.length; i++) {
+                removeResources((ResourceHandler) handlers[i].getKey(), (Filter) handlers[i].getValue());
+            }
+            
+            System.out.println("DESTROY..." + m_handlers.size());
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceDependencyTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceDependencyTest.java?rev=1579067&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceDependencyTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ResourceDependencyTest.java Tue Mar 18 21:47:17 2014
@@ -0,0 +1,215 @@
+package dm.it;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import junit.framework.Assert;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+import dm.Component;
+import dm.DependencyManager;
+import dm.ResourceHandler;
+import dm.ResourceUtil;
+
+public class ResourceDependencyTest extends TestBase {
+    public void testResourceDependency() {
+        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
+        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(e);
+        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.changeResource();
+        
+        // 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();
+    }
+    
+    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 ResourceProvider {
+        private volatile BundleContext m_context;
+        private final Ensure m_ensure;
+        private final Map<ResourceHandler, Filter> m_handlers = new HashMap<ResourceHandler, Filter>();
+        private URL[] m_resources;
+        
+        public ResourceProvider(Ensure ensure) {
+            m_ensure = ensure;
+            try {
+                m_resources = new URL[] {
+                    new URL("file://localhost/path/to/file1.txt"),
+                    new URL("file://localhost/path/to/file2.txt"),
+                    new URL("file://localhost/path/to/file3.doc")
+                };
+            }
+            catch (MalformedURLException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+        
+        public void add(ServiceReference ref, ResourceHandler handler) {
+            String filterString = (String) ref.getProperty("filter");
+            Filter filter;
+            try {
+                filter = m_context.createFilter(filterString);
+            }
+            catch (InvalidSyntaxException e) {
+                Assert.fail("Could not create filter for resource handler: " + e);
+                return;
+            }
+            synchronized (m_handlers) {
+                m_handlers.put(handler, filter);
+            }
+            for (int i = 0; i < m_resources.length; i++) {
+                if (filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+                    handler.added(m_resources[i]);
+                }
+            }
+        }
+
+        public void changeResource() {
+            Filter filter;
+            for (Entry<ResourceHandler, Filter> entry : m_handlers.entrySet()) {
+                for (int i = 0; i < m_resources.length; i++) {
+                    if (i == 0) {
+                        if (entry.getValue().match(ResourceUtil.createProperties(m_resources[i]))) {
+                            entry.getKey().changed(m_resources[i]); 
+                        }
+                    }
+                }
+            }
+        }
+        
+        public void remove(ServiceReference ref, ResourceHandler handler) {
+            debug("ResourceProvider.remove(ref=%s, handler=%s, handlers=%s", ref, handler, m_handlers);
+            Filter filter;
+            synchronized (m_handlers) {
+                filter = (Filter) m_handlers.remove(handler);
+            }
+            removeResources(handler, filter);
+        }
+
+        private void removeResources(ResourceHandler handler, Filter filter) {
+            for (int i = 0; i < m_resources.length; i++) {
+                if (filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+                    handler.removed(m_resources[i]);
+                }
+            }
+        }
+
+        public void destroy() {
+            debug("ResourceProvider.destroy: handlers=%s", m_handlers);
+            Entry[] handlers;
+            synchronized (m_handlers) {
+                handlers = (Entry[]) m_handlers.entrySet().toArray(new Entry[m_handlers.size()]);
+            }
+            for (int i = 0; i < handlers.length; i++) {
+                removeResources((ResourceHandler) handlers[i].getKey(), (Filter) handlers[i].getValue());
+            }
+        }
+    }
+    
+    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);
+        }
+    } 
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ServiceUpdateTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ServiceUpdateTest.java?rev=1579067&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ServiceUpdateTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/ServiceUpdateTest.java Tue Mar 18 21:47:17 2014
@@ -0,0 +1,220 @@
+package dm.it;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+import junit.framework.Assert;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+import dm.Component;
+import dm.DependencyManager;
+import dm.ResourceHandler;
+import dm.ResourceUtil;
+
+public class ServiceUpdateTest extends TestBase {
+    public void testServiceUpdate() throws Exception {
+        DependencyManager m = new DependencyManager(context);
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a resource provider
+        ResourceProvider provider = new ResourceProvider(e);
+        // 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);
+        Properties serviceProps = new Properties();
+        serviceProps.setProperty("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.
+        private Ensure m_ensure;
+        
+        ResourceAdapter(Ensure e) {
+            m_ensure = e;
+        }
+
+        public void invoke() {
+            // TODO Auto-generated method stub
+            
+        }
+    }
+    
+    static class ResourceProvider {
+        private volatile BundleContext m_context;
+        private final Ensure m_ensure;
+        private final Map m_handlers = new HashMap();
+        private URL[] m_resources;
+
+        public ResourceProvider(Ensure ensure) throws MalformedURLException {
+            m_ensure = ensure;
+            m_resources = new URL[] {
+                new URL("file://localhost/path/to/file1.txt")
+            };
+        }
+        
+        public void change() {
+            ResourceHandler[] handlers;
+            synchronized (m_handlers) {
+                handlers = (ResourceHandler[]) m_handlers.keySet().toArray(new ResourceHandler[m_handlers.size()]);
+            }
+            for (int i = 0; i < m_resources.length; i++) {
+                for (int j = 0; j < handlers.length; j++) {
+                    ResourceHandler handler = handlers[j];
+                    handler.changed(m_resources[i]);
+                }
+            }
+        }
+
+        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;
+                }
+            }
+            synchronized (m_handlers) {
+                m_handlers.put(handler, filter);
+            }
+            for (int i = 0; i < m_resources.length; i++) {
+                if (filter == null || filter.match(ResourceUtil.createProperties(m_resources[i]))) {
+                    handler.added(m_resources[i]);
+                }
+            }
+        }
+
+        public void remove(ServiceReference ref, ResourceHandler handler) {
+            Filter filter;
+            synchronized (m_handlers) {
+                filter = (Filter) m_handlers.remove(handler);
+            }
+            removeResources(handler, filter);
+        }
+
+        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() {
+            Entry[] handlers;
+            synchronized (m_handlers) {
+                handlers = (Entry[]) m_handlers.entrySet().toArray(new Entry[m_handlers.size()]);
+            }
+            for (int i = 0; i < handlers.length; i++) {
+                removeResources((ResourceHandler) handlers[i].getKey(), (Filter) handlers[i].getValue());
+            }
+        }
+    }
+    
+    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 dict = component.getServiceProperties();
+            Enumeration e = dict.keys();
+            while (e.hasMoreElements()) {
+                String key = (String) e.nextElement();
+                String value = (String) dict.get(key);
+                newProps.setProperty(key, value);
+            }
+            newProps.setProperty("new-property", "2");
+            component.getServiceRegistration().setProperties(newProps);
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/TemporalServiceDependencyTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/TemporalServiceDependencyTest.java?rev=1579067&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/TemporalServiceDependencyTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm.it/src/dm/it/TemporalServiceDependencyTest.java Tue Mar 18 21:47:17 2014
@@ -0,0 +1,82 @@
+package dm.it;
+
+import dm.Component;
+import dm.DependencyManager;
+
+public class TemporalServiceDependencyTest extends TestBase {
+    public void testServiceConsumptionAndIntermittentAvailability() {
+        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
+        Component sp = m.createComponent().setImplementation(new TemporalServiceProvider(e)).setInterface(TemporalServiceInterface.class.getName(), null);
+        Component sp2 = m.createComponent().setImplementation(new TemporalServiceProvider2(e)).setInterface(TemporalServiceInterface.class.getName(), null);
+        Component sc = m.createComponent().setImplementation(new TemporalServiceConsumer(e)).add(m.createTemporalServiceDependency().setService(TemporalServiceInterface.class).setRequired(true));
+        // add the service consumer
+        m.add(sc);
+        // now add the first provider
+        m.add(sp);
+        e.waitForStep(2, 15000);
+        // 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, 15000);
+        // 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);
+    }
+
+    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 {
+        private final Ensure m_ensure;
+        public TemporalServiceProvider2(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke() {
+            m_ensure.step(4);
+        }
+    }
+
+    static class TemporalServiceConsumer implements Runnable {
+        private volatile TemporalServiceInterface m_service;
+        private 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);
+        }
+    }
+}