felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pde...@apache.org
Subject svn commit: r1564995 [16/17] - in /felix/sandbox/pderop/dependencymanager: ./ core/ core/.externalToolBuilders/ core/.settings/ core/src/ core/src/main/ core/src/main/java/ core/src/main/java/org/ core/src/main/java/org/apache/ core/src/main/java/org/a...
Date Wed, 05 Feb 2014 23:22:36 GMT
Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ResourceAdapterDependencyAddAndRemoveTest2.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ResourceAdapterDependencyAddAndRemoveTest2.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ResourceAdapterDependencyAddAndRemoveTest2.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ResourceAdapterDependencyAddAndRemoveTest2.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,272 @@
+/*
+ * 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.test.integration.api;
+
+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.apache.felix.dm.Component;
+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.ResourceUtil;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.test.components.Ensure;
+import org.apache.felix.dm.test.integration.common.TestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+@RunWith(PaxExam.class)
+public class ResourceAdapterDependencyAddAndRemoveTest2 extends TestBase {
+    @Test
+    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
+        ResourceAdapter service = new ResourceAdapter(e);
+        CallbackInstance callbackInstance = new CallbackInstance(e);
+        Component component = m.createResourceAdapterService("(&(path=/path/to/*.txt)(host=localhost))", false, callbackInstance, "changed")
+            .setImplementation(new ResourceAdapter(e))
+            .setCallbacks(callbackInstance, "init", "start", "stop", "destroy")
+            .add(m.createServiceDependency()
+      			.setService(ServiceInterface.class, "(id=1)")
+      			.setRequired(true)
+      			.setInstanceBound(true)
+  			);
+        component.addStateListener(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;
+        
+        ResourceAdapter(Ensure e) {
+            m_ensure = e;
+        }
+    }
+    
+    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;
+    	
+    	public CallbackInstance(Ensure e) {
+    		m_ensure = e;
+    	}
+    	
+    	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 = null;
+    		
+    		for (Object dependency : component.getDependencies()) {
+    			if (dependency instanceof ServiceDependency) {
+    				// remove the dependency
+    				oldDependency = (Dependency) dependency;
+    				System.out.println("Old dependency props: " + oldDependency.getProperties());
+    			}
+    		}
+    		
+    		// and add a new dependency
+    		component.add(component.getDependencyManager().createServiceDependency().setService(ServiceInterface.class, "(id=2)").setRequired(true).setInstanceBound(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;
+    	}
+
+		public void started(Component c) {
+			System.out.println("started");
+		}
+
+		public void starting(Component c) {
+			System.out.println("starting");
+		}
+
+		public void stopped(Component c) {
+			System.out.println("stopped");
+		}
+
+		public void stopping(Component c) {
+			System.out.println("stopping");
+		}
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ResourceAdapterTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ResourceAdapterTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ResourceAdapterTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ResourceAdapterTest.java Wed Feb  5 23:22:32 2014
@@ -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.test.integration.api;
+
+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.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.ResourceUtil;
+import org.apache.felix.dm.test.components.Ensure;
+import org.apache.felix.dm.test.integration.common.TestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+@RunWith(PaxExam.class)
+public class ResourceAdapterTest extends TestBase {
+    @Test
+    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/test/src/test/java/org/apache/felix/dm/test/integration/api/ResourceDependencyTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ResourceDependencyTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ResourceDependencyTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ResourceDependencyTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,235 @@
+/*
+ * 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.test.integration.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 java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.ResourceUtil;
+import org.apache.felix.dm.test.components.Ensure;
+import org.apache.felix.dm.test.integration.common.TestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+@RunWith(PaxExam.class)
+public class ResourceDependencyTest extends TestBase {
+    @Test
+    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();
+    }
+    
+    static class ResourceConsumer {
+        private volatile int m_counter;
+        private Ensure m_ensure;
+        
+        public ResourceConsumer(Ensure ensure) {
+            m_ensure = ensure;
+        }
+        
+        public void add(URL resource) {
+            m_counter++;
+            m_ensure.step();
+        }
+        public void change(URL resource) {
+            m_ensure.step();
+        }
+        public void remove(URL 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);
+        }
+    }
+    
+    static 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) {
+            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() {
+            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 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/test/src/test/java/org/apache/felix/dm/test/integration/api/SerialExecutorTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/SerialExecutorTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/SerialExecutorTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/SerialExecutorTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,139 @@
+/*
+ * 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.test.integration.api;
+
+import static org.mockito.Mockito.mock;
+
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.impl.Logger;
+import org.apache.felix.dm.impl.SerialExecutor;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Validates SerialExecutor used by DM implementation.
+ */
+public class SerialExecutorTest {
+    final Random m_rnd = new Random();
+    
+    @Test
+    public void testSerialExecutor() {
+        System.out.println("Testing serial executor");
+        int cores = Math.max(10, Runtime.getRuntime().availableProcessors());
+        ExecutorService threadPool = null;
+
+        try {
+            threadPool = Executors.newFixedThreadPool(cores);
+            BundleContext bctx = mock(BundleContext.class);
+            final SerialExecutor serial = new SerialExecutor(new Logger(bctx));
+            final int TESTS = 100000;
+
+            long timeStamp = System.currentTimeMillis();
+            for (int i = 0; i < TESTS; i++) {
+                final CountDownLatch latch = new CountDownLatch(cores * 2 /* each task reexecutes itself one time */);
+                for (int j = 0; j < cores; j ++) {
+                    threadPool.execute(new Runnable() {
+                        public void run() {
+                            serial.enqueue(new SerialTask(serial, latch));
+                            serial.execute();
+                        }
+                    });
+                }
+                Assert.assertTrue("Test " + i + " did not terminate timely", latch.await(20000, TimeUnit.MILLISECONDS));
+            }
+            long now = System.currentTimeMillis();
+            System.out.println("Performed " + TESTS + " tests in " + (now - timeStamp) + " ms.");
+            timeStamp = now;
+        }
+
+        catch (Throwable t) {
+            t.printStackTrace();
+            Assert.fail("Test failed: " + t.getMessage());
+        }
+        finally {
+            shutdown(threadPool);
+        }
+    }
+
+    void shutdown(ExecutorService exec) {
+        exec.shutdown();
+        try {
+            exec.awaitTermination(5, TimeUnit.SECONDS);
+        }
+        catch (InterruptedException e) {
+        }
+    }
+
+    class SerialTask implements Runnable {
+        final AtomicReference<Thread> m_executingThread = new AtomicReference<Thread>();
+        final CountDownLatch m_latch;
+        private boolean m_firstExecution;
+        private final SerialExecutor m_exec;          
+        
+        SerialTask(SerialExecutor exec, CountDownLatch latch) {
+            m_latch = latch;
+            m_exec = exec;
+            m_firstExecution = true;
+        }
+
+        public void run() {
+            Thread self = Thread.currentThread();
+            if (m_firstExecution) {
+                if (!m_executingThread.compareAndSet(null, self)) {
+                    System.out.println("detected concurrent call to SerialTask: currThread=" + self
+                        + ", other executing thread=" + m_executingThread);
+                    return;
+                }
+            } else {
+                if (m_executingThread.get() != self) {
+                    System.out.println("expect to execute reentrant tasks in same thread, but current thread=" + self
+                        + ", while expected is " + m_executingThread);
+                    return;
+                }
+            }
+            
+            try {
+                Thread.sleep(m_rnd.nextInt(1));
+            }
+            catch (InterruptedException e) {
+            }
+            
+            if (m_firstExecution) {
+                m_firstExecution = false;
+                m_exec.executeNow(this); // Our run method must be called immediately
+            } else {
+                if (! m_executingThread.compareAndSet(self, null)) {
+                    System.out.println("detected concurrent call to SerialTask: currThread=" + self
+                        + ", other executing thread=" + m_executingThread);
+                    return;
+                }
+            }
+            
+            m_latch.countDown();
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyComponentLifeCycleTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyComponentLifeCycleTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyComponentLifeCycleTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyComponentLifeCycleTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,158 @@
+/*
+ * 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.test.integration.api;
+//import static org.ops4j.pax.exam.CoreOptions.waitForFrameworkStartupFor;
+//import static org.ops4j.pax.exam.container.def.PaxRunnerOptions.vmOption;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.test.components.Ensure;
+import org.apache.felix.dm.test.integration.common.TestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.util.tracker.ServiceTracker;
+
+@RunWith(PaxExam.class)
+public class ServiceDependencyComponentLifeCycleTest extends TestBase {
+    @Test
+    public void testComponentLifeCycleWhenAddingAndRemovingDependencies() 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
+        
+        Component component = m.createComponent().setInterface(MyService2.class.getName(), null).setImplementation(new MyComponent(e));
+        ServiceDependency dependency = m.createServiceDependency().setService(MyService.class).setRequired(true);
+        ServiceDependency dependency2 = m.createServiceDependency().setService(MyService.class).setRequired(true);
+        ServiceTracker st = new ServiceTracker(context, MyService2.class.getName(), null);
+        st.open();
+        Component component2 = m.createComponent().setInterface(MyService.class.getName(), null).setImplementation(new MyImpl(e));
+        
+        // add the component: it has no dependencies so it should be activated immediately
+        m.add(component);
+        Assert.assertNotNull("service should be available", st.getService());
+                
+        // add a required dependency that is not available, so the component should be deactivated
+        component.add(dependency);
+        Assert.assertNull("service should no longer be available", st.getService());
+        // remove the dependency again, so the component should be activated again
+        component.remove(dependency);
+        Assert.assertNotNull("service should be available", st.getService());
+        // make the dependency instance bound
+        dependency.setInstanceBound(true);
+        
+        // add it again, the component was already active so even though the dependency
+        // is required, the component will *NOT* go through the destroy life cycle methods
+        component.add(dependency);
+        Assert.assertNull("service should no longer be available", st.getService());
+        component.remove(dependency);
+        Assert.assertNotNull("service should be available", st.getService());
+        
+        // make the second dependency instance bound too
+        dependency2.setInstanceBound(true);
+        
+        // activate the service we depend on
+        m.add(component2);
+        // init and start should be invoked here, so wait for them to complete
+        e.waitForStep(10, 5000);
+        
+        component.add(dependency);
+        Assert.assertNotNull("service should be available", st.getService());
+        component.add(dependency2);
+        Assert.assertNotNull("service should be available", st.getService());
+        component.remove(dependency);
+        Assert.assertNotNull("service should be available", st.getService());
+        
+        e.step(11);
+        
+        // remove the service again, the component still has an instance bound
+        // dependency on it, so stop() should be invoked, but the component
+        // should not be destroyed
+        m.remove(component2);
+        e.step(15);
+        Assert.assertNull("service should no longer be available", st.getService());
+        component.remove(dependency2);
+        Assert.assertNotNull("service should be available", st.getService());
+        m.remove(component);
+        e.step(19);
+    }
+    
+    public static class MyComponent implements MyService2 {
+        private final Ensure m_ensure;
+        private final Ensure.Steps m_initSteps = new Ensure.Steps(1, 5);
+        private final Ensure.Steps m_startSteps = new Ensure.Steps(2, 6, 8, 16);
+        private final Ensure.Steps m_stopSteps = new Ensure.Steps(3, 7, 12, 17);
+        private final Ensure.Steps m_destroySteps = new Ensure.Steps(4, 18);
+
+        public MyComponent(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            System.out.println("init");
+            m_ensure.steps(m_initSteps);
+        }
+        
+        public void start() {
+            System.out.println("start");
+            m_ensure.steps(m_startSteps);
+        }
+        
+        public void stop() {
+            System.out.println("stop");
+            m_ensure.steps(m_stopSteps);
+        }
+
+        public void destroy() {
+            System.out.println("destroy");
+            m_ensure.steps(m_destroySteps);
+        }
+    }
+    
+    public static interface MyService2 {
+    }
+    
+    public static interface MyService {
+    }
+    
+    public static class MyImpl implements MyService {
+        private final Ensure m_ensure;
+
+        public MyImpl(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            m_ensure.step(9);
+        }
+        public void start() {
+            m_ensure.step(10);
+        }
+        public void stop() {
+            m_ensure.step(13);
+        }
+        public void destroy() {
+            m_ensure.step(14);
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyInjectionTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyInjectionTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyInjectionTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyInjectionTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,115 @@
+/*
+ * 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.test.integration.api;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.test.components.Ensure;
+import org.apache.felix.dm.test.integration.common.TestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+
+@RunWith(PaxExam.class)
+public class ServiceDependencyInjectionTest extends TestBase {
+    @Test
+    public void testServiceInjection() {
+        DependencyManager m = new DependencyManager(context);
+        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()
+            .setImplementation(new ServiceConsumerNamedInjection())
+            .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"))
+            ;
+        m.add(sp);
+        m.add(sc);
+        m.remove(sc);
+        m.add(sc2);
+        m.remove(sc2);
+        m.remove(sp);
+        e.waitForStep(5, 5000);
+    }
+    
+    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);
+        private Ensure.Steps m_invoke2Steps = new Ensure.Steps(1, 2, 3);
+        
+        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);
+        }
+    }
+
+    static class ServiceConsumerNamedInjection {
+        private volatile ServiceInterface2 m_service;
+        private volatile ServiceInterface m_service2;
+        private volatile Object m_service3;
+
+        public void init() {
+            // 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);
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyPropagateTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyPropagateTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyPropagateTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyPropagateTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,136 @@
+/*
+ * 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.test.integration.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.test.components.Ensure;
+import org.apache.felix.dm.test.integration.common.TestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Validates ServiceDependency service properties propagation.
+ */
+@RunWith(PaxExam.class)
+public class ServiceDependencyPropagateTest extends TestBase {
+    /**
+     * Checks that a ServiceDependency propagates the dependency service properties to the provided service properties.
+     */
+    @Test
+    public void testServiceDependencyPropagate() {
+        DependencyManager m = new DependencyManager(context);
+        // 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"); }})
+                      .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.
+     */
+    @Test
+    public void testServiceDependencyPropagateCallback() {
+        DependencyManager m = new DependencyManager(context);
+        // 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"))) {
+                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"); }};
+      }
+    }
+    
+    public static class C3 {
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,96 @@
+/*
+ * 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.test.integration.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.test.components.Ensure;
+import org.apache.felix.dm.test.integration.common.TestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+
+@RunWith(PaxExam.class)
+public class ServiceDependencyTest extends TestBase {
+    @Test
+    public void testServiceRegistrationAndConsumption() {
+        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 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);
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyThroughCallbackInstanceTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyThroughCallbackInstanceTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyThroughCallbackInstanceTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceDependencyThroughCallbackInstanceTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,94 @@
+/*
+ * 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.test.integration.api;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.test.integration.common.TestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.BundleContext;
+
+@RunWith(PaxExam.class)
+public class ServiceDependencyThroughCallbackInstanceTest extends TestBase {
+    @Test
+    public void testServiceWithCallbacksAndOneDependency() {
+        invokeTest(context, 1);
+    }
+    
+    @Test
+    public void testServiceWithCallbacksAndThreeDependencies() {
+        invokeTest(context, 3);
+    }
+
+    private void invokeTest(BundleContext context, int numberOfServices) {
+        DependencyManager m = new DependencyManager(context);
+        // 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;
+    	}
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceRaceTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceRaceTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceRaceTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceRaceTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,412 @@
+/*
+ * 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.test.integration.api;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.test.components.Ensure;
+import org.apache.felix.dm.test.integration.common.TestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Another race test for concurrent service registrations/unregistrations.
+ * Aspects are also depending on some configuration pids, which are also registered/unregistered concurrently.
+ */
+@RunWith(PaxExam.class)
+public class ServiceRaceTest extends TestBase {
+    final static int STEP_WAIT = 10000;
+    final static int SERVICES = 3;
+    final static int INVOKES = 10;
+    volatile ExecutorService m_execServices; // used to register/unregister S services
+    volatile ExecutorService m_execAspects; // used to register/unregister Aspects
+
+    @Inject
+    volatile ConfigurationAdmin m_ca;
+
+    @Test
+    public void testConcurrentServices() {
+        warn("starting concurrent services");
+        int cores = Math.max(10, Runtime.getRuntime().availableProcessors());
+        final DependencyManager dm = new DependencyManager(context);
+        Random rnd = new Random();
+
+        try {
+            m_execServices = Executors.newFixedThreadPool(cores);
+            m_execAspects = Executors.newFixedThreadPool(cores);
+            int serviceIdCounter = 1;
+            long timeStamp = System.currentTimeMillis();
+            final int tests = 30000;
+            for (int loop = 0; loop < tests; loop++) {
+                debug("loop#%d -------------------------", (loop + 1));
+
+                final Ensure clientStarted = new Ensure(false);
+                final Ensure clientStopped = new Ensure(false);
+                final Ensure servicesStopped = new Ensure(false);
+                final Ensure servicesInvoked = new Ensure(false);
+                final Ensure aspectsInvoked = new Ensure(false);
+                final Ensure aspectsRemoved = new Ensure(false);
+
+                // Create one client depending on many S services
+                Client client = new Client(clientStarted, clientStopped);
+                Component c = dm.createComponent().setImplementation(client);
+                for (int i = 0; i < SERVICES; i++) {
+                    String filter = "(name=S" + i + "-" + serviceIdCounter + ")";
+                    c.add(dm.createServiceDependency().setService(S.class, filter).setRequired(true).setCallbacks(
+                        "add", null, "remove", "swap"));
+                }
+                dm.add(c);
+
+                // Create S services concurrently
+                info("registering S services concurrently");
+                final ConcurrentLinkedQueue<Component> services = new ConcurrentLinkedQueue<Component>();
+                for (int i = 0; i < SERVICES; i++) {
+                    final String name = "S" + i + "-" + serviceIdCounter;
+                    Hashtable h = new Hashtable();
+                    h.put("name", name);
+                    final Component sImpl = dm.createComponent().setImplementation(
+                        new SImpl(servicesStopped, servicesInvoked, name)).setInterface(
+                        S.class.getName(), h);
+                    services.add(sImpl);
+                    m_execServices.execute(new Runnable() {
+                        public void run() {
+                            dm.add(sImpl);
+                        }
+                    });
+                }
+
+                // Create S aspects concurrently
+                info("registering aspects concurrently");
+                final Queue<Component> aspects = new ConcurrentLinkedQueue<Component>();
+                for (int i = 0; i < SERVICES; i++) {
+                    final String name = "Aspect" + i + "-" + serviceIdCounter;
+                    final String filter = "(name=S" + i + "-" + serviceIdCounter + ")";
+                    final String pid = "Aspect" + i + "-" + serviceIdCounter + ".pid";
+                    final int rank = (i+1);
+                    SAspect sa = new SAspect(aspectsInvoked, name, rank);
+                    debug("Adding aspect " + sa);
+                    final Component aspect = dm.createAspectService(S.class, filter, rank).setImplementation(sa).add(
+                        dm.createConfigurationDependency().setPid(pid));
+                    aspects.add(aspect);
+                    m_execAspects.execute(new Runnable() {
+                        public void run() {
+                            dm.add(aspect);
+                        }
+                    });
+                }
+                
+                // Provide all aspect configuration (asynchronously)
+                final Queue<Configuration> aspectPids = new ConcurrentLinkedQueue<Configuration>();
+                for (int i = 0; i < SERVICES; i++) {
+                    final String name = "Aspect" + i + "-" + serviceIdCounter;
+                    final String pid = "Aspect" + i + "-" + serviceIdCounter + ".pid";
+                    final int rank = (i+1);
+                    SAspect sa = new SAspect(aspectsInvoked, name, rank);
+                    debug("Adding aspect configuration pid %s for aspect %s", pid, sa);
+                    try {
+                        Configuration aspectConf = m_ca.getConfiguration(pid, null);
+                        aspectConf.update(new Hashtable() {
+                            {
+                                put("name", name);
+                            }
+                        });
+                        aspectPids.add(aspectConf);
+                    }
+                    catch (IOException e) {
+                        error("could not create pid %s for aspect %s", e, pid, name);
+                    }
+                }
+                
+                // Increment service id counter, for next iteration.
+                serviceIdCounter ++;
+
+                // Make sure client is started
+                clientStarted.waitForStep(1, STEP_WAIT);
+                info("all services have been started");
+
+                // Make sure client invoked services SERVICES * INVOKES times
+                servicesInvoked.waitForStep(SERVICES * INVOKES, STEP_WAIT);
+                info("All services have been properly invoked");
+                
+                // Now ensure that some random aspects have been randomly invoked.
+                int aspectCount = Math.min(1, rnd.nextInt(SERVICES));
+                int aspectInvocations = Math.min(1, rnd.nextInt(INVOKES));                
+                aspectsInvoked.waitForStep(aspectCount * aspectInvocations, STEP_WAIT);
+                info("%d aspects have been properly invoked %d times", aspectCount, aspectInvocations);                                
+                                
+                // Unregister services concurrently (at this point, it is possible that we have still some aspects being activating !
+                info("unregistering services concurrently");
+                for (final Component sImpl : services) {
+                    m_execServices.execute(new Runnable() {
+                        public void run() {
+                            dm.remove(sImpl);
+                        }
+                    });
+                }
+
+                // unregister aspects concurrently (some aspects can potentially be still activating !)
+                info("unregistering aspects concurrently");
+                for (final Component a : aspects) {
+                    m_execAspects.execute(new Runnable() {
+                        public void run() {
+                            debug("removing aspect %s", a);
+                            dm.remove(a);
+                            aspectsRemoved.step();
+                        }
+                    });
+                }
+                
+                info("unregistering aspects configuration concurrently");
+                for (Configuration aspectConf : aspectPids) {
+                    aspectConf.delete(); // asynchronous
+                }
+
+                info("removing client");
+                dm.remove(c);
+
+                // Wait until all services have been stopped
+                servicesStopped.waitForStep(SERVICES, STEP_WAIT);
+                
+                // Wait until client has been stopped
+                clientStopped.waitForStep(1, STEP_WAIT);
+                
+                // Wait until all aspects have been deleted
+                aspectsRemoved.waitForStep(SERVICES, STEP_WAIT);
+                
+                if (super.errorsLogged()) {
+                    throw new IllegalStateException("Race test interrupted (some error occured, see previous logs)");
+                }
+
+                info("finished one test loop: current components=%d", dm.getComponents().size());
+                if ((loop + 1) % 100 == 0) {
+                    long duration = System.currentTimeMillis() - timeStamp;
+                    warn("Performed %d tests (total=%d) in %d ms.", (loop + 1), tests, duration);
+                    timeStamp = System.currentTimeMillis();
+                }
+                
+                // Cleanup all remaining components (but all components should have been removed now).
+                dm.clear();
+            }
+        }
+
+        catch (Throwable t) {
+            error("Test failed", t);
+            Assert.fail("Test failed: " + t.getMessage());
+        }
+        finally {
+            shutdown(m_execServices);
+            shutdown(m_execAspects);
+            dm.clear();
+        }
+    }
+
+    void shutdown(ExecutorService exec) {
+        exec.shutdown();
+        try {
+            exec.awaitTermination(5, TimeUnit.SECONDS);
+        }
+        catch (InterruptedException e) {
+        }
+    }
+
+    public interface S {
+        void invoke(int prevAspectId);
+    }
+
+    public class SImpl implements S {
+        final Ensure m_stopped, m_invoked;
+        final String m_name;
+
+        public SImpl(Ensure stopped, Ensure invoked, String name) {
+            m_name = name;
+            m_stopped = stopped;
+            m_invoked = invoked;
+        }
+
+        public void invoke(int prevRank) {
+            Assert.assertTrue(prevRank > 0);
+            m_invoked.step();
+        }
+
+        public String toString() {
+            return m_name;
+        }
+
+        public void start() {
+            info("started %s", this);
+        }
+
+        public void stop() {
+            info("stopped %s", this);
+            m_stopped.step();
+        }
+    }
+
+    public class Client implements Runnable {
+        final Ensure m_started, m_stopped;
+        final Map<String, S> m_services = new ConcurrentHashMap<String, S>();
+        volatile Thread m_thread;
+        volatile Exception m_firstStartStackTrace;
+        volatile boolean m_running;
+
+        Client(Ensure started, Ensure stopped) {
+            m_started = started;
+            m_stopped = stopped;
+        }
+
+        synchronized void swap(ServiceReference prevRef, S prev, ServiceReference nextRef, S next) {
+            info("client.swap: prev=%s, next=%s", prev, next);
+            m_services.put((String) nextRef.getProperty("name"), next);
+        }
+
+        synchronized void add(Map<String, String> props, S s) {
+            info("client.add: %s", s);
+            m_services.put(props.get("name"), s);
+        }
+
+        synchronized void remove(Map<String, String> props, S s) {
+            info("client.remove: %s", s);
+            m_services.remove(props.get("name"));
+        }
+
+        public synchronized void start() {
+            if (m_firstStartStackTrace != null) {
+                error("client already started", new Exception());
+                error("first start was done here:", m_firstStartStackTrace);
+                return;
+            }
+            if (m_running) {
+                error("Client already started");
+                return;
+            }
+            if (m_services.size() != SERVICES) {
+                error("Client started with unexpected number of injected services: %s", m_services);
+                return;
+            }
+            m_firstStartStackTrace = new Exception("First start stacktrace");
+            info("client starting");
+
+            m_thread = new Thread(this, "Client");
+            m_thread.setDaemon(true);
+            m_running = true;
+            m_thread.start();
+        }
+
+        public void run() {
+            m_started.step();
+            while (m_running) {
+                for (int i = 0; i < INVOKES; i++) {
+                    for (Map.Entry<String, S> e : m_services.entrySet()) {
+                        e.getValue().invoke(Integer.MAX_VALUE); // We are on the top of the aspect list
+                    }
+                }
+            }
+        }
+
+        public synchronized void stop() {
+            if (!m_running) {
+                error("client can't be stopped (not running)");
+                Thread.dumpStack();
+                return;
+            }
+
+            info("stopping client");
+            m_running = false;
+            try {
+                m_thread.join();
+            }
+            catch (InterruptedException e) {
+                error("interrupted while stopping client", e);
+            }
+            info("client stopped");
+            m_firstStartStackTrace = null;
+            m_stopped.step();
+        }
+    }
+
+    public class SAspect implements S {
+        volatile S m_next;
+        final String m_name;
+        final int m_rank;
+        final Ensure m_invoked;
+        volatile Dictionary<String, String> m_conf;
+
+        SAspect(Ensure invoked, String name, int rank) {
+            m_invoked = invoked;
+            m_name = name;
+            m_rank = rank;
+        }
+
+        public void updated(Dictionary<String, String> conf) {
+            if (conf == null) {
+                info("Aspect %s injected with a null configuration", this);
+                return;
+            }
+            debug("Aspect %s injected with configuration: %s", this, conf);
+            m_conf = conf;
+        }
+
+        public void start() {
+            info("started aspect %s", this);
+        }
+
+        public void stop() {
+            info("stopped aspect %s", this);
+        }
+
+        public void invoke(int prevRank) {
+            Assert.assertTrue(prevRank > m_rank);
+            if (m_conf == null) {
+                error("Aspect: %s has not been injected with its configuration", this);
+                return;
+            }
+
+            if (!m_name.equals(m_conf.get("name"))) {
+                error("Aspect %s has not been injected with expected configuration: %s", this, m_conf);
+                return;
+            }
+            m_invoked.step();
+            m_next.invoke(m_rank);
+        }
+
+        public String toString() {
+            return m_name;
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceTrackerTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceTrackerTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceTrackerTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceTrackerTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,117 @@
+/*
+ * 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.test.integration.api;
+
+import java.util.Hashtable;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ServiceUtil;
+import org.apache.felix.dm.test.integration.common.TestBase;
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+
+@RunWith(PaxExam.class)
+public class ServiceTrackerTest extends TestBase {
+    @Test
+    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();
+    }
+    
+    @Test
+    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();
+    }
+    
+    @Test
+    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() {
+        }
+    }
+}



Mime
View raw message