felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pde...@apache.org
Subject svn commit: r1564995 [13/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/AspectAwareServiceDependencyTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectAwareServiceDependencyTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectAwareServiceDependencyTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectAwareServiceDependencyTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.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 AspectAwareServiceDependencyTest 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 ServiceConsumerCallbacks(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(false).setCallbacks("add", "change", "remove", "swap"));
+        Component asp = m.createAspectService(ServiceInterface.class, null, 1000, null).setImplementation(ServiceProviderAspect.class);
+        m.add(sp);
+        m.add(sc);
+        m.add(asp);        
+        m.remove(sc);
+        m.remove(sp);
+        // ensure we executed all steps inside the component instance
+        e.step(4);
+    }
+    
+    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 ServiceConsumerCallbacks {
+        private final Ensure m_ensure;
+
+        public ServiceConsumerCallbacks(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void add(ServiceInterface service) {
+            m_ensure.step(1);
+        }
+        public void remove(ServiceInterface service) {
+            m_ensure.step(3);
+        }
+        public void swap(ServiceInterface oldService, ServiceInterface newService) {
+        	m_ensure.step(2);
+        }
+    }
+    
+    static class ServiceProviderAspect implements ServiceInterface {
+    	private volatile ServiceProvider serviceProvider;
+
+    	public ServiceProviderAspect() {
+    	}
+    	
+		public void invoke() {
+			serviceProvider.invoke();
+		}
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectBaseTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectBaseTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectBaseTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectBaseTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,225 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ServiceUtil;
+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.framework.ServiceRegistration;
+
+@RunWith(PaxExam.class)
+public class AspectBaseTest extends TestBase {    
+    @Test
+    public void testSingleAspect() {
+        DependencyManager m = new DependencyManager(context);
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        
+        // create a service provider and consumer
+        ServiceProvider p = new ServiceProvider(e, "a");
+        ServiceConsumer c = new ServiceConsumer(e);
+        Component sp = m.createComponent()
+            .setInterface(ServiceInterface.class.getName(), new Properties() {{ put("name", "a"); }})
+            .setImplementation(p);
+        Component sc = m.createComponent()
+            .setImplementation(c)
+            .add(m.createServiceDependency()
+                .setService(ServiceInterface.class)
+                .setRequired(true)
+                .setCallbacks("add", "remove")
+                .setAutoConfig(true)
+            );
+        Component sa = m.createAspectService(ServiceInterface.class, null, 20, null)
+            .setImplementation(ServiceAspect.class);
+        m.add(sc);
+        m.add(sp);
+        // after the provider was added, the consumer's add should have been invoked once
+        e.waitForStep(1, 2000);
+        Assert.assertEquals("a", c.invoke());
+        m.add(sa);
+        // after the aspect was added, the consumer should get and add for the aspect and a remove
+        // for the original service
+        e.waitForStep(3, 2000);
+        Assert.assertEquals("aa", c.invoke());
+        m.remove(sa);
+        // removing the aspect again should give a remove and add
+        e.waitForStep(5, 2000);
+        Assert.assertEquals("a", c.invoke());
+        m.remove(sp);
+        // finally removing the original service should give a remove
+        e.waitForStep(6, 2000);
+        m.remove(sc);
+        e.step(7);
+    }
+    
+    @Test
+    public void testSingleAspectThatAlreadyExisted() {
+        DependencyManager m = new DependencyManager(context);
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        
+        // create a service provider and consumer
+        ServiceProvider p = new ServiceProvider(e, "a");
+        ServiceConsumer c = new ServiceConsumer(e);
+        Component sp = m.createComponent().setImplementation(p).setInterface(ServiceInterface.class.getName(), new Properties() {{ put("name", "a"); }});
+        Component sc = m.createComponent().setImplementation(c).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", "remove").setAutoConfig(true));
+        Component sa = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(ServiceAspect.class);
+        // we first add the aspect
+        m.add(sa);
+        // then the service provider
+        m.add(sp);
+        // finally the consumer
+        m.add(sc);
+
+        Assert.assertEquals("aa", c.invoke());
+        
+        // now the consumer's added should be invoked once, as the aspect is already available and should
+        // directly hide the original service
+        e.waitForStep(1, 2000);
+        e.step(2);
+
+        m.remove(sa);
+        // after removing the aspect, the consumer should get the original service back, so
+        // remove and add will be invoked
+        e.waitForStep(4, 2000);
+        
+        Assert.assertEquals("a", c.invoke());
+        
+        m.remove(sp);
+        // after removing the original service, the consumer's remove should be called once
+        e.waitForStep(5, 2000);
+        
+        m.remove(sc);
+        e.step(6);
+    }
+
+    @Test
+    public void testMultipleAspects() {
+        DependencyManager m = new DependencyManager(context);
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        
+        // create service providers and consumers
+        ServiceConsumer c = new ServiceConsumer(e);
+        Component sp = m.createComponent().setImplementation(new ServiceProvider(e, "a")).setInterface(ServiceInterface.class.getName(), new Properties() {{ put("name", "a"); }});
+        Component sp2 = m.createComponent().setImplementation(new ServiceProvider(e, "b")).setInterface(ServiceInterface.class.getName(), new Properties() {{ put("name", "b"); }});
+        Component sc = m.createComponent().setImplementation(c).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true).setCallbacks("add", "remove"));
+        Component sa = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(ServiceAspect.class);
+        Component sa2 = m.createAspectService(ServiceInterface.class, null, 10, null).setImplementation(ServiceAspect.class);
+        m.add(sp);
+        m.add(sp2);
+        m.add(sa);
+        m.add(sa2);
+        m.add(sc);
+        // the consumer will monitor progress, it should get it's add invoked twice, once for every
+        // (highest) aspect
+        e.waitForStep(2, 2000);
+        e.step(3);
+        
+        // now invoke all services the consumer collected
+        List<String> list = c.invokeAll();
+        // and make sure both of them are correctly invoked
+        Assert.assertTrue(list.size() == 2);
+        Assert.assertTrue(list.contains("aaa"));
+        Assert.assertTrue(list.contains("bbb"));
+        
+        m.remove(sc);
+        // removing the consumer now should get its removed method invoked twice
+        e.waitForStep(5, 2000);
+        e.step(6);
+        m.remove(sa2);
+        m.remove(sa);
+        m.remove(sp2);
+        m.remove(sp);
+        e.step(7);
+    }
+    
+    public static interface ServiceInterface {
+        public String invoke(String input);
+    }
+    
+    public static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        private final String m_name;
+        public ServiceProvider(Ensure e, String name) {
+            m_ensure = e;
+            m_name = name;
+        }
+        public String invoke(String input) {
+            return input + m_name;
+        }
+    }
+    
+    public static class ServiceAspect implements ServiceInterface {
+        private volatile ServiceInterface m_originalService;
+        private volatile ServiceRegistration m_registration;
+        
+        public String invoke(String input) {
+            String result = m_originalService.invoke(input);
+            String property = (String) m_registration.getReference().getProperty("name");
+            return result + property;
+        }
+    }
+
+    public static class ServiceConsumer {
+        private final Ensure m_ensure;
+        private volatile ServiceInterface m_service;
+        private List<ServiceInterface> m_services = new ArrayList<ServiceInterface>();
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void add(ServiceReference ref, ServiceInterface si) {
+            System.out.println("add: " + ServiceUtil.toString(ref));
+            m_services.add(si);
+            m_ensure.step();
+        }
+        
+        public void remove(ServiceReference ref, ServiceInterface si) {
+            System.out.println("rem: " + ServiceUtil.toString(ref));
+            m_services.remove(si);
+            m_ensure.step();
+        }
+        
+        public String invoke() {
+            return m_service.invoke("");
+        }
+        
+        public List<String> invokeAll() {
+            List<String> results = new ArrayList<String>();
+            for (ServiceInterface si : m_services) {
+                results.add(si.invoke(""));
+            }
+            return results;
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectChainTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectChainTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectChainTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectChainTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.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 AspectChainTest extends TestBase {
+    @Test
+    public void testBuildAspectChain() {
+        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 sa2 = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(new ServiceAspect(e, 3));
+        Component sa3 = m.createAspectService(ServiceInterface.class, null, 30, null).setImplementation(new ServiceAspect(e, 2));
+        Component sa1 = m.createAspectService(ServiceInterface.class, null, 10, null).setImplementation(new ServiceAspect(e, 4));
+        m.add(sc);
+
+        m.add(sp);
+        m.add(sa2);
+        m.add(sa3);
+        m.add(sa1);
+        e.step();
+        
+        m.remove(sa3);
+        m.remove(sa2);
+        m.remove(sa1);
+        m.remove(sp);
+        
+        m.remove(sc);
+    }
+    
+    static interface ServiceInterface {
+        public void invoke(Runnable run);
+    }
+    
+    static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke(Runnable run) {
+            run.run();
+        }
+    }
+    
+    static class ServiceAspect implements ServiceInterface {
+        private final Ensure m_ensure;
+        private volatile ServiceInterface m_parentService;
+        private final int m_step;
+        
+        public ServiceAspect(Ensure e, int step) {
+            m_ensure = e;
+            m_step = step;
+        }
+        public void start() {
+        }
+        
+        public void invoke(Runnable run) {
+            m_ensure.step(m_step);
+            m_parentService.invoke(run);
+        }
+        
+        public void stop() {
+        }
+    }
+
+    static class ServiceConsumer implements Runnable {
+        private volatile ServiceInterface m_service;
+        private final Ensure m_ensure;
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            Thread t = new Thread(this);
+            t.start();
+        }
+        
+        public void run() {
+            m_ensure.waitForStep(1, 2000);
+            m_service.invoke(Ensure.createRunnableStep(m_ensure, 5));
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectDynamicsTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectDynamicsTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectDynamicsTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectDynamicsTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,171 @@
+/*
+ * 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 AspectDynamicsTest extends TestBase {
+    @Test
+    public void testDynamicallyAddAndRemoveAspect() {
+        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 provider = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component provider2 = m.createComponent().setImplementation(new ServiceProvider2(e)).setInterface(ServiceInterface2.class.getName(), null);
+        Component consumer = m.createComponent().setImplementation(new ServiceConsumer(e)).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true));
+        Component aspect = m.createAspectService(ServiceInterface.class, null, 1, null).setImplementation(new ServiceAspect(e));
+        
+        m.add(consumer);
+        m.add(provider);
+        // the consumer should invoke the provider here, and when done, arrive at step 3
+        // finally wait for step 6 before continuing
+        e.waitForStep(3, 15000);
+        
+        m.add(aspect);
+        // after adding the aspect, we wait for its init to be invoked, arriving at
+        // step 4 after an instance bound dependency was added (on a service provided by
+        // provider 2)
+        e.waitForStep(4, 15000);
+        
+        m.add(provider2);
+        
+        // after adding provider 2, we should now see the aspect being started, so
+        // we wait for step 5 to happen
+        e.waitForStep(5, 15000);
+        
+        // now we continue with step 6, which will trigger the next part of the consumer's
+        // run method to be executed
+        e.step(6);
+        
+        // invoking step 7, 8 and 9 when invoking the aspect which in turn invokes the
+        // dependency and the original service, so we wait for that to finish here, which
+        // is after step 10 has been reached (the client will now wait for step 12)
+        e.waitForStep(10, 15000);
+        
+        m.remove(aspect);
+        // removing the aspect should trigger step 11 (in the stop method of the aspect)
+        e.waitForStep(11, 15000);
+        
+        // step 12 triggers the client to continue
+        e.step(12);
+        
+        // wait for step 13, the final invocation of the provided service (without aspect)
+        e.waitForStep(13, 15000);
+        
+        // clean up
+        m.remove(provider2);
+        m.remove(provider);
+        m.remove(consumer);
+    }
+    
+    static interface ServiceInterface {
+        public void invoke(Runnable run);
+    }
+    
+    static interface ServiceInterface2 {
+        public void invoke();
+    }
+    
+    static class ServiceProvider2 implements ServiceInterface2 {
+        private final Ensure m_ensure;
+
+        public ServiceProvider2(Ensure ensure) {
+            m_ensure = ensure;
+        }
+
+        public void invoke() {
+            m_ensure.step(9);
+        }
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke(Runnable run) {
+            run.run();
+        }
+    }
+    
+    static class ServiceAspect implements ServiceInterface {
+        private final Ensure m_ensure;
+        private volatile ServiceInterface m_originalService;
+        private volatile ServiceInterface2 m_injectedService;
+        private volatile Component m_service;
+        private volatile DependencyManager m_manager;
+        
+        public ServiceAspect(Ensure e) {
+            m_ensure = e;
+        }
+        public void init() {
+            m_service.add(m_manager.createServiceDependency()
+                .setService(ServiceInterface2.class)
+                .setRequired(true)
+                .setInstanceBound(true)
+            );
+            m_ensure.step(4);
+        }
+        public void start() {
+            m_ensure.step(5);
+        }
+        public void invoke(Runnable run) {
+            m_ensure.step(7);
+            m_originalService.invoke(run);
+            m_injectedService.invoke();
+        }
+        
+        public void stop() {
+            m_ensure.step(11);
+        }
+    }
+
+    static class ServiceConsumer implements Runnable {
+        private volatile ServiceInterface m_service;
+        private final Ensure m_ensure;
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            Thread t = new Thread(this);
+            t.start();
+        }
+        
+        public void run() {
+            m_ensure.step(1);
+            m_service.invoke(Ensure.createRunnableStep(m_ensure, 2));
+            m_ensure.step(3);
+            m_ensure.waitForStep(6, 15000);
+            m_service.invoke(Ensure.createRunnableStep(m_ensure, 8));
+            m_ensure.step(10);
+            m_ensure.waitForStep(12, 15000);
+            m_service.invoke(Ensure.createRunnableStep(m_ensure, 13));
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectRaceTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectRaceTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectRaceTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectRaceTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,336 @@
+/*
+ * 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.HashSet;
+import java.util.Hashtable;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+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.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+@RunWith(PaxExam.class)
+public class AspectRaceTest extends TestBase {
+	volatile ExecutorService m_serviceExec;
+	volatile ExecutorService m_aspectExec;
+	volatile DependencyManager m_dm;
+	final static int SERVICES = 3;
+	final static int ASPECTS_PER_SERVICE = 10;
+
+	@Test
+	public void testConcurrentAspects() {
+		try {
+			warn("starting aspect race test");
+			int cores = Math.max(4, Runtime.getRuntime().availableProcessors());
+			// Used to inject S services
+			m_serviceExec = Executors.newFixedThreadPool(cores);
+			// Used to inject S Aspects
+			m_aspectExec = Executors.newFixedThreadPool(cores);
+
+			// Setup test components using dependency manager.
+			// We create a Controller which is injected with some S services,
+			// and each S services has some aspects (SAspect).
+
+			m_dm = new DependencyManager(context);
+			Controller controller = new Controller();
+			Component c = m_dm
+					.createComponent()
+					.setImplementation(controller)
+					.setInterface(Controller.class.getName(), null)
+					.setComposition("getComposition")
+					.add(m_dm.createServiceDependency().setService(S.class)
+							.setCallbacks("bind", null, "unbind", "swap")
+							.setRequired(true));
+
+			m_dm.add(c);
+
+			for (int loop = 1; loop <= 3000; loop++) {
+				// Perform concurrent injections of "S" service and S aspects
+				// into the Controller component;
+				Factory f = new Factory();
+				f.register();
+
+				controller.check();
+
+				// unregister all services and aspects concurrently
+				f.unregister();
+
+				if ((loop) % 100 == 0) {
+					warn("Performed " + loop + " tests.");
+				}				
+
+	            if (super.errorsLogged()) {
+	                throw new IllegalStateException("Race test interrupted (some error occured, see previous logs)");
+	            }
+			}
+		}
+
+		catch (Throwable t) {
+			error("Test failed", t);
+			Assert.fail("Test failed: " + t.getMessage());
+		} finally {
+			shutdown(m_serviceExec);
+			shutdown(m_aspectExec);
+			m_dm.clear();
+		}
+	}
+
+	void shutdown(ExecutorService exec) {
+		exec.shutdown();
+		try {
+			exec.awaitTermination(5, TimeUnit.SECONDS);
+		} catch (InterruptedException e) {
+		}
+	}
+
+	public interface S {
+		void invoke(Ensure e);
+	}
+
+	public static class SImpl implements S {
+		final int m_step;
+
+		SImpl(int step) {
+			m_step = step;
+		}
+
+		public void invoke(Ensure e) {
+			e.step(m_step);
+		}
+
+		public String toString() {
+			return "SImpl[" + m_step + "]";
+		}
+	}
+
+	public static class SAspect implements S {
+		volatile S m_next;
+		final int m_rank;
+		final int m_step;
+
+		SAspect(int rank) {
+			m_rank = rank;
+			m_step = ASPECTS_PER_SERVICE - rank + 1;
+		}
+
+		public void added(S s) {
+			m_next = s;
+		}
+
+		public void swap(S oldS, S newS) {
+			m_next = newS;
+		}
+
+		public void invoke(Ensure e) {
+			e.step(m_step);
+			m_next.invoke(e);
+		}
+
+		public String toString() {
+			return "SAspect[" + m_rank + ", " + m_step + "]";
+		}
+	}
+
+	class Factory {
+		final ConcurrentLinkedQueue<Component> m_services = new ConcurrentLinkedQueue<Component>();
+		final ConcurrentLinkedQueue<Component> m_aspects = new ConcurrentLinkedQueue<Component>();
+
+		public void register() throws InterruptedException {
+			final CountDownLatch latch = new CountDownLatch(SERVICES
+					+ (ASPECTS_PER_SERVICE * SERVICES));
+
+			for (int i = 1; i <= SERVICES; i++) {
+				final int serviceId = i;
+				m_serviceExec.execute(new Runnable() {
+					public void run() {
+						try {
+							Component c = m_dm.createComponent();
+							Hashtable<String, String> props = new Hashtable<String, String>();
+							props.put("id", String.valueOf(serviceId));
+							c.setInterface(S.class.getName(), props)
+									.setImplementation(
+											new SImpl(ASPECTS_PER_SERVICE + 1));
+							m_services.add(c);
+							m_dm.add(c);
+							latch.countDown();
+						} catch (Throwable e) {
+							error(e);
+						}
+					}
+				});
+
+				for (int j = 1; j <= ASPECTS_PER_SERVICE; j++) {
+					final int rank = j;
+					m_aspectExec.execute(new Runnable() {
+						public void run() {
+							try {
+								SAspect sa = new SAspect(rank);
+								Component aspect = m_dm.createAspectService(
+										S.class, "(id=" + serviceId + ")",
+										rank, "added", null, null, "swap")
+										.setImplementation(sa);
+								debug("adding aspect " + sa);
+								m_aspects.add(aspect);
+								m_dm.add(aspect);
+								latch.countDown();
+							} catch (Throwable e) {
+								error(e);
+							}
+						}
+					});
+				}
+			}
+
+			if (!latch.await(5000, TimeUnit.MILLISECONDS)) {
+				throw new IllegalStateException(
+						"could not register services and aspects timely");
+			}
+
+			debug("all registered: aspects=" + m_aspects);
+			// Thread.sleep(5000);
+		}
+
+		public void unregister() throws InterruptedException,
+				InvalidSyntaxException {
+			final CountDownLatch latch = new CountDownLatch(SERVICES
+					+ (ASPECTS_PER_SERVICE * SERVICES));
+
+			unregisterAspects(latch);
+			unregisterServices(latch);
+
+			if (!latch.await(5000, TimeUnit.MILLISECONDS)) {
+				throw new IllegalStateException(
+						"could not unregister services and aspects timely");
+			}
+
+			if (context.getServiceReference(S.class.getName()) != null) {
+				error("could not unregister some services or aspects !");
+			}
+			debug("unregistered all aspects and services concurrently");
+		}
+
+		public void unregisterAspects(final CountDownLatch latch)
+				throws InterruptedException, InvalidSyntaxException {
+			Component c;
+			debug("unregister: aspects=" + m_aspects);
+
+			while ((c = m_aspects.poll()) != null) {
+				final Component c$ = c;
+				m_serviceExec.execute(new Runnable() {
+					public void run() {
+						try {
+							debug("removing service " + c$);
+							m_dm.remove(c$);
+							latch.countDown();
+						} catch (Throwable e) {
+							error(e);
+						}
+					}
+				});
+			}
+		}
+
+		public void unregisterServices(final CountDownLatch latch)
+				throws InterruptedException {
+			Component c;
+			debug("unregister: services=" + m_services);
+
+			while ((c = m_services.poll()) != null) {
+				final Component c$ = c;
+				m_serviceExec.execute(new Runnable() {
+					public void run() {
+						try {
+							debug("removing service " + c$);
+							m_dm.remove(c$);
+							latch.countDown();
+						} catch (Throwable e) {
+							error(e);
+						}
+					}
+				});
+			}
+
+			debug("unregistered all services");
+		}
+	}
+
+	public class Controller {
+		final Composition m_compo = new Composition();
+		final HashSet<S> m_services = new HashSet<S>();
+
+		Object[] getComposition() {
+			return new Object[] { this, m_compo };
+		}
+
+		void bind(ServiceReference sr) {
+			S s = (S) sr.getBundle().getBundleContext().getService(sr);
+			if (s == null) {
+				throw new IllegalStateException(
+						"bindA: bundleContext.getService returned null");
+			}
+			debug("bind " + s);
+			synchronized (this) {
+				m_services.add(s);
+			}
+		}
+
+		void swap(S previous, S current) {
+			debug("swap: " + previous + "," + current);
+			synchronized (this) {
+				if (!m_services.remove(previous)) {
+					error("swap: unknow previous service: " + previous);
+				}
+				m_services.add(current);
+			}
+		}
+
+		void unbind(S a) {
+			debug("unbind " + a);
+			synchronized (this) {
+				m_services.remove(a);
+			}
+		}
+
+		void check() {
+			synchronized (this) {
+				for (S s : m_services) {
+					debug("checking service: " + s + " ...");
+					Ensure ensure = new Ensure(false);
+					s.invoke(ensure);
+				}
+			}
+		}
+	}
+
+	public static class Composition {
+	}
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectWhiteboardTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectWhiteboardTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectWhiteboardTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectWhiteboardTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,202 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ServiceUtil;
+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.Constants;
+import org.osgi.framework.ServiceReference;
+
+@RunWith(PaxExam.class)
+public class AspectWhiteboardTest extends TestBase {
+    @Test
+    public void testWhiteboardConsumer() {
+        DependencyManager m = new DependencyManager(context);
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create service providers and consumer
+        Component sp1 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        Component sp2 = m.createComponent().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
+        ServiceConsumer sci = new ServiceConsumer(e);
+        Component sc = m.createComponent().setImplementation(sci).add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(false).setCallbacks("add", "remove"));
+        Component sa2 = m.createAspectService(ServiceInterface.class, null, 20, null).setImplementation(new ServiceAspect(e, 3));
+        Component sa1 = m.createAspectService(ServiceInterface.class, null, 10, null).setImplementation(new ServiceAspect(e, 4));
+        
+        // start with a service consumer
+        System.out.println("Adding consumer");
+        m.add(sc);
+        
+        // then add two providers, so the consumer will see two services
+        System.out.println("Adding 2 providers");
+        m.add(sp1);
+        m.add(sp2);
+        
+        // make sure consumer sees both services
+        Assert.assertEquals(2, sci.services());
+        
+        // add an aspect with ranking 20
+        System.out.println("Adding aspect with rank 20");
+        m.add(sa2);
+        
+        // make sure the consumer sees the two new aspects and no longer sees the two original services
+        Assert.assertEquals(2, sci.services());
+        Assert.assertEquals(20, sci.highestRanking());
+        Assert.assertEquals(20, sci.lowestRanking());
+        
+        // add an aspect with ranking 10
+        System.out.println("Adding aspect with rank 10");
+        m.add(sa1);
+        
+        // make sure the consumer still sees the two aspects with ranking 20
+        Assert.assertEquals(2, sci.services());
+        Assert.assertEquals(20, sci.highestRanking());
+        Assert.assertEquals(20, sci.lowestRanking());
+        
+        // remove the aspect with ranking 20
+        System.out.println("Removing aspect with rank 20");
+        m.remove(sa2);
+        
+        // make sure the consumer now sees the aspects with ranking 10
+        Assert.assertEquals(2, sci.services());
+        Assert.assertEquals(10, sci.highestRanking());
+        Assert.assertEquals(10, sci.lowestRanking());
+        
+        // remove one of the original services
+        System.out.println("Removing 1 service");
+        m.remove(sp1);
+        
+        // make sure the aspect of that service goes away
+        Assert.assertEquals(1, sci.services());
+        Assert.assertEquals(10, sci.highestRanking());
+        Assert.assertEquals(10, sci.lowestRanking());
+        
+        // remove the aspect with ranking 10
+        System.out.println("Removing aspect with rank 10");
+        m.remove(sa1);
+        
+        // make sure only the original service remains
+        Assert.assertEquals(1, sci.services());
+        Assert.assertEquals(0, sci.highestRanking());
+        Assert.assertEquals(0, sci.lowestRanking());
+
+        System.out.println("Done with test");
+
+        // end of test
+        m.remove(sa2);
+        m.remove(sp2);
+        m.remove(sc);
+    }
+    
+    static interface ServiceInterface {
+        public void invoke(Runnable run);
+    }
+    
+    static class ServiceProvider implements ServiceInterface {
+        private final Ensure m_ensure;
+        public ServiceProvider(Ensure e) {
+            m_ensure = e;
+        }
+        public void invoke(Runnable run) {
+            run.run();
+        }
+    }
+    
+    static class ServiceAspect implements ServiceInterface {
+        private final Ensure m_ensure;
+        private volatile ServiceInterface m_parentService;
+        private final int m_step;
+        
+        public ServiceAspect(Ensure e, int step) {
+            m_ensure = e;
+            m_step = step;
+        }
+        public void start() {
+        }
+        
+        public void invoke(Runnable run) {
+            m_ensure.step(m_step);
+            m_parentService.invoke(run);
+        }
+        
+        public void stop() {
+        }
+    }
+
+    static class ServiceConsumer implements Runnable {
+        private List m_services = new ArrayList();
+        private final Ensure m_ensure;
+
+        public ServiceConsumer(Ensure e) {
+            m_ensure = e;
+        }
+        
+        public void init() {
+            Thread t = new Thread(this);
+            t.start();
+        }
+        
+        public void run() {
+        }
+        
+        public int services() {
+            return m_services.size();
+        }
+        
+        public int highestRanking() {
+            int ranking = Integer.MIN_VALUE;
+            for (int i = 0; i < m_services.size(); i++) {
+                ServiceReference ref = (ServiceReference) m_services.get(i);
+                Integer r = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
+                int rank = r == null ? 0 : r.intValue();
+                ranking = Math.max(ranking, rank);
+            }
+            return ranking;
+        }
+        public int lowestRanking() {
+            int ranking = Integer.MAX_VALUE;
+            for (int i = 0; i < m_services.size(); i++) {
+                ServiceReference ref = (ServiceReference) m_services.get(i);
+                Integer r = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
+                int rank = r == null ? 0 : r.intValue();
+                ranking = Math.min(ranking, rank);
+            }
+            return ranking;
+        }
+        
+        public void add(ServiceReference ref, ServiceInterface svc) {
+            System.out.println("Added: " + ServiceUtil.toString(ref));
+            m_services.add(ref);
+        }
+        public void remove(ServiceReference ref, ServiceInterface svc) {
+            System.out.println("Removed: " + ServiceUtil.toString(ref));
+            m_services.remove(ref);
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectWithPropagationTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectWithPropagationTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectWithPropagationTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/AspectWithPropagationTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,686 @@
+/*
+ * 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.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ServiceUtil;
+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.framework.ServiceRegistration;
+
+/**
+ * Test for aspects with service properties propagations.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@RunWith(PaxExam.class)
+public class AspectWithPropagationTest extends TestBase {
+    private final static int ASPECTS = 3;
+    private final Set<Integer> _randoms = new HashSet<Integer>();
+    private final Random _rnd = new Random();
+    private static Ensure m_invokeStep;
+    private static Ensure m_changeStep;
+    
+    /**
+     * This test does the following:
+     * 
+     * - Create S service
+     * - Create some S Aspects
+     * - Create a Client, depending on S (actually, on the top-level S aspect)
+     * - Client has a "change" callack in order to track S service properties modifications.
+     * - First, invoke Client.invoke(): all S aspects, and finally original S service must be invoked orderly.
+     * - Modify S original service properties, and check if all aspects, and the client has been orderly called in their "change" callback.
+     * - Modify the First lowest ranked aspect (rank=1), and check if all aspects, and client have been orderly called in their "change" callback.
+     */
+    @Test
+    public void testAspectsWithPropagation() {
+        System.out.println("----------- Running testAspectsWithPropagation ...");
+        DependencyManager m = new DependencyManager(context);
+        // helper class that ensures certain steps get executed in sequence
+        m_invokeStep = new Ensure(); 
+        
+        // Create our original "S" service.
+        Dictionary props = new Hashtable();
+        props.put("foo", "bar");
+        Component s = m.createComponent()
+                .setImplementation(new SImpl())
+                .setInterface(S.class.getName(), props);
+        
+        // Create an aspect aware client, depending on "S" service.
+        Client clientImpl;
+        Component client = m.createComponent()
+                .setImplementation((clientImpl = new Client()))
+                .add(m.createServiceDependency()
+                     .setService(S.class)
+                     .setRequired(true)
+                     .setDebug("client")
+                     .setCallbacks("add", "change", "remove", "swap"));
+
+        // Create some "S" aspects
+        Component[] aspects = new Component[ASPECTS];
+        for (int rank = 1; rank <= ASPECTS; rank ++) {
+            aspects[rank-1] = m.createAspectService(S.class, null, rank, "add", "change", "remove", "swap")
+                    .setImplementation(new A("A" + rank, rank));
+            props = new Hashtable();
+            props.put("a" + rank, "v" + rank);
+            aspects[rank-1].setServiceProperties(props);
+        }                                    
+              
+        // Register client
+        m.add(client);
+        
+        // Randomly register aspects and original service
+        boolean originalServiceAdded = false;
+        for (int i = 0; i < ASPECTS; i ++) {
+            int index = getRandomAspect();
+            m.add(aspects[index]);
+            if (_rnd.nextBoolean()) {
+                m.add(s);
+                originalServiceAdded = true;
+            }
+        }
+        if (! originalServiceAdded) {
+            m.add(s);
+        }
+              
+        // All set, check if client has inherited from top level aspect properties + original service properties
+        Map check = new HashMap();
+        check.put("foo", "bar");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS);
+        checkServiceProperties(check, clientImpl.getServiceProperties());
+
+        // Now invoke client, which orderly calls all aspects in the chain, and finally the original service "S".
+        System.out.println("-------------------------- Invoking client.");
+        clientImpl.invoke();
+        m_invokeStep.waitForStep(ASPECTS+1, 5000);
+        
+        // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, and on client. 
+        System.out.println("-------------------------- Modifying original service properties.");
+        m_changeStep = new Ensure();
+        props = new Hashtable();
+        props.put("foo", "barModified");
+        s.setServiceProperties(props);
+        
+        // Check if aspects and client have been orderly called in their "changed" callback
+        m_changeStep.waitForStep(ASPECTS+1, 5000);
+        
+        // Check if modified "foo" original service property has been propagated
+        check = new HashMap();
+        check.put("foo", "barModified");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS); // we only see top-level aspect service properties
+        checkServiceProperties(check, clientImpl.getServiceProperties());
+        
+        // Now, change the top-level ranked aspect: it must propagate to all upper aspects, as well as to the client
+        System.out.println("-------------------------- Modifying top-level aspect service properties.");
+
+        m_changeStep = new Ensure();
+        for (int i = 1; i <= ASPECTS; i ++) {
+            m_changeStep.step(i); // only client has to be changed.
+        }
+        props = new Hashtable();
+        props.put("a" + ASPECTS, "v" + ASPECTS + "-Modified");
+        aspects[ASPECTS-1].setServiceProperties(props); // That triggers change callbacks for upper aspects (with rank >= 2)
+        m_changeStep.waitForStep(ASPECTS+1, 5000); // check if client have been changed.
+        
+        // Check if top level aspect service properties have been propagated up to the client.
+        check = new HashMap();
+        check.put("foo", "barModified");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS + "-Modified");
+        checkServiceProperties(check, clientImpl.getServiceProperties());
+
+        // Clear all components.
+        m_changeStep = null;
+        m.clear();
+    }    
+    
+    /**
+     * This test does the following:
+     * 
+     * - Create S service
+     * - Create some S Aspects without any callbacks (add/change/remove/swap)
+     * - Create a Client, depending on S (actually, on the top-level S aspect)
+     * - Client has a "change" callack in order to track S service properties modifications.
+     * - First, invoke Client.invoke(): all S aspects, and finally original S service must be invoked orderly.
+     * - Modify S original service properties, and check if the client has been called in its "change" callback.
+     */
+    @Test
+    public void testAspectsWithPropagationAndNoCallbacks() {
+        System.out.println("----------- Running testAspectsWithPropagation ...");
+        DependencyManager m = new DependencyManager(context);
+        // helper class that ensures certain steps get executed in sequence
+        m_invokeStep = new Ensure(); 
+        
+        // Create our original "S" service.
+        Dictionary props = new Hashtable();
+        props.put("foo", "bar");
+        Component s = m.createComponent()
+                .setImplementation(new SImpl())
+                .setInterface(S.class.getName(), props);
+        
+        // Create an aspect aware client, depending on "S" service.
+        Client clientImpl;
+        Component client = m.createComponent()
+                .setImplementation((clientImpl = new Client()))
+                .add(m.createServiceDependency()
+                     .setService(S.class)
+                     .setRequired(true)
+                     .setDebug("client")
+                     .setCallbacks("add", "change", "remove"));
+
+        // Create some "S" aspects
+        Component[] aspects = new Component[ASPECTS];
+        for (int rank = 1; rank <= ASPECTS; rank ++) {
+            aspects[rank-1] = m.createAspectService(S.class, null, rank)
+                    .setImplementation(new A("A" + rank, rank));
+            props = new Hashtable();
+            props.put("a" + rank, "v" + rank);
+            aspects[rank-1].setServiceProperties(props);
+        }                                    
+              
+        // Register client
+        m.add(client);
+        
+        // Randomly register aspects and original service
+        boolean originalServiceAdded = false;
+        for (int i = 0; i < ASPECTS; i ++) {
+            int index = getRandomAspect();
+            m.add(aspects[index]);
+            if (_rnd.nextBoolean()) {
+                m.add(s);
+                originalServiceAdded = true;
+            }
+        }
+        if (! originalServiceAdded) {
+            m.add(s);
+        }
+              
+        // All set, check if client has inherited from top level aspect properties + original service properties
+        Map check = new HashMap();
+        check.put("foo", "bar");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS);
+        checkServiceProperties(check, clientImpl.getServiceProperties());
+
+        // Now invoke client, which orderly calls all aspects in the chain, and finally the original service "S".
+        System.out.println("-------------------------- Invoking client.");
+        clientImpl.invoke();
+        m_invokeStep.waitForStep(ASPECTS+1, 5000);
+        
+        // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, and on client. 
+        System.out.println("-------------------------- Modifying original service properties.");
+        m_changeStep = new Ensure();
+        for (int i = 1; i <= ASPECTS; i ++) {
+            m_changeStep.step(i); // skip aspects, which have no "change" callbacks.
+        }
+        props = new Hashtable();
+        props.put("foo", "barModified");
+        s.setServiceProperties(props);
+        
+        // Check if aspects and client have been orderly called in their "changed" callback
+        m_changeStep.waitForStep(ASPECTS+1, 5000);
+        
+        // Check if modified "foo" original service property has been propagated
+        check = new HashMap();
+        check.put("foo", "barModified");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS); // we only see top-level aspect service properties
+        checkServiceProperties(check, clientImpl.getServiceProperties());
+        
+        // Clear all components.
+        m_changeStep = null;
+        m.clear();
+    }    
+    
+    /**
+     * This test does the following:
+     * 
+     * - Create S service
+     * - Create some S Aspects
+     * - Create S2 Adapter, which adapts S to S2
+     * - Create Client2, which depends on S2. Client2 listens to S2 property change events.
+     * - Now, invoke Client2.invoke(): all S aspects, and finally original S service must be invoked orderly.
+     * - Modify S original service properties, and check if all aspects, S2 Adapter, and Client2 have been orderly called in their "change" callback.
+     */
+    @Test
+    public void testAdapterWithAspectsAndPropagation() {
+        System.out.println("----------- Running testAdapterWithAspectsAndPropagation ...");
+
+        DependencyManager m = new DependencyManager(context);
+        m_invokeStep = new Ensure(); 
+        
+        // Create our original "S" service.
+        Dictionary props = new Hashtable();
+        props.put("foo", "bar");
+        Component s = m.createComponent()
+                .setImplementation(new SImpl())
+                .setInterface(S.class.getName(), props);
+        
+        // Create some "S" aspects
+        Component[] aspects = new Component[ASPECTS];
+        for (int rank = 1; rank <= ASPECTS; rank ++) {
+            aspects[rank-1] = m.createAspectService(S.class, null, rank, "add", "change", "remove", "swap")
+                    .setImplementation(new A("A" + rank, rank));
+            props = new Hashtable();
+            props.put("a" + rank, "v" + rank);
+            aspects[rank-1].setServiceProperties(props);
+        } 
+        
+        // Create S2 adapter (which adapts S1 to S2 interface)
+        Component adapter = m.createAdapterService(S.class, null, "add", "change", "remove", "swap")
+                .setInterface(S2.class.getName(), null)
+                .setImplementation(new S2Impl());
+        
+        // Create Client2, which depends on "S2" service.
+        Client2 client2Impl;
+        Component client2 = m.createComponent()
+                .setImplementation((client2Impl = new Client2()))
+                .add(m.createServiceDependency()
+                     .setService(S2.class)
+                     .setRequired(true)
+                     .setDebug("client")                     
+                     .setCallbacks("add", "change", null));
+              
+        // Register client2
+        m.add(client2);
+        
+        // Register S2 adapter
+        m.add(adapter);
+        
+        // Randomly register aspects, original service
+        boolean originalServiceAdded = false;
+        for (int i = 0; i < ASPECTS; i ++) {
+            int index = getRandomAspect();
+            m.add(aspects[index]);
+            if (_rnd.nextBoolean()) {
+                m.add(s);
+                originalServiceAdded = true;
+            }
+        }
+        if (! originalServiceAdded) {
+            m.add(s);
+        }
+             
+        // Now invoke client2, which orderly calls all S1 aspects, then S1Impl, and finally S2 service
+        System.out.println("-------------------------- Invoking client2.");
+        client2Impl.invoke2();
+        m_invokeStep.waitForStep(ASPECTS+2, 5000);
+        
+        // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, S2Impl, and Client2.
+        System.out.println("-------------------------- Modifying original service properties.");
+        m_changeStep = new Ensure();
+        props = new Hashtable();
+        props.put("foo", "barModified");
+        s.setServiceProperties(props);
+        
+        // Check if aspects and Client2 have been orderly called in their "changed" callback
+        m_changeStep.waitForStep(ASPECTS+2, 5000);
+        
+        // Check if modified "foo" original service property has been propagated to Client2
+        Map check = new HashMap();
+        check.put("foo", "barModified");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS);
+        checkServiceProperties(check, client2Impl.getServiceProperties());
+        
+        // Clear all components.
+        m_changeStep = null;
+        m.clear();
+    }    
+    
+    /**
+     * This test does the following:
+     * 
+     * - Create S service
+     * - Create some S Aspects without any callbacks (add/change/remove)
+     * - Create S2 Adapter, which adapts S to S2 (but does not have any add/change/remove callbacks)
+     * - Create Client2, which depends on S2. Client2 listens to S2 property change events.
+     * - Now, invoke Client2.invoke(): all S aspects, and finally original S service must be invoked orderly.
+     * - Modify S original service properties, and check if all aspects, S2 Adapter, and Client2 have been orderly called in their "change" callback.
+     */
+    @Test
+    public void testAdapterWithAspectsAndPropagationNoCallbacks() {
+        System.out.println("----------- Running testAdapterWithAspectsAndPropagationNoCallbacks ...");
+
+        DependencyManager m = new DependencyManager(context);
+        m_invokeStep = new Ensure(); 
+        
+        // Create our original "S" service.
+        Dictionary props = new Hashtable();
+        props.put("foo", "bar");
+        Component s = m.createComponent()
+                .setImplementation(new SImpl())
+                .setInterface(S.class.getName(), props);
+        
+        // Create some "S" aspects
+        Component[] aspects = new Component[ASPECTS];
+        for (int rank = 1; rank <= ASPECTS; rank ++) {
+            aspects[rank-1] = m.createAspectService(S.class, null, rank)
+                    .setImplementation(new A("A" + rank, rank));
+            props = new Hashtable();
+            props.put("a" + rank, "v" + rank);
+            aspects[rank-1].setServiceProperties(props);
+        } 
+        
+        // Create S2 adapter (which adapts S1 to S2 interface)
+        Component adapter = m.createAdapterService(S.class, null)
+                .setInterface(S2.class.getName(), null)
+                .setImplementation(new S2Impl());
+        
+        // Create Client2, which depends on "S2" service.
+        Client2 client2Impl;
+        Component client2 = m.createComponent()
+                .setImplementation((client2Impl = new Client2()))
+                .add(m.createServiceDependency()
+                     .setService(S2.class)
+                     .setRequired(true)
+                     .setDebug("client")                     
+                     .setCallbacks("add", "change", "remove"));
+              
+        // Register client2
+        m.add(client2);
+        
+        // Register S2 adapter
+        m.add(adapter);
+        
+        // Randomly register aspects, original service
+        boolean originalServiceAdded = false;
+        for (int i = 0; i < ASPECTS; i ++) {
+            int index = getRandomAspect();
+            m.add(aspects[index]);
+            if (_rnd.nextBoolean()) {
+                m.add(s);
+                originalServiceAdded = true;
+            }
+        }
+        if (! originalServiceAdded) {
+            m.add(s);
+        }
+             
+        // Now invoke client2, which orderly calls all S1 aspects, then S1Impl, and finally S2 service
+        System.out.println("-------------------------- Invoking client2.");
+        client2Impl.invoke2();
+        m_invokeStep.waitForStep(ASPECTS+2, 5000);
+        
+        // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, S2Impl, and Client2.
+        System.out.println("-------------------------- Modifying original service properties.");
+        m_changeStep = new Ensure();
+        for (int i = 1; i <= ASPECTS+1; i ++) {
+            m_changeStep.step(i); // skip all aspects and the adapter
+        }
+        props = new Hashtable();
+        props.put("foo", "barModified");
+        s.setServiceProperties(props);
+        
+        // Check if Client2 has been called in its "changed" callback
+        m_changeStep.waitForStep(ASPECTS+2, 5000);
+        
+        // Check if modified "foo" original service property has been propagated to Client2
+        Map check = new HashMap();
+        check.put("foo", "barModified");
+        for (int i = 1; i < (ASPECTS - 1); i ++) {
+            check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect.
+        }
+        check.put("a" + ASPECTS, "v" + ASPECTS);
+        checkServiceProperties(check, client2Impl.getServiceProperties());
+        
+        // Clear all components.
+        m_changeStep = null;
+        m.clear();
+    }    
+    
+   private void checkServiceProperties(Map<?, ?> check, Dictionary properties) {
+        for (Object key : check.keySet()) {
+            Object val = check.get(key);   
+            if (val == null) {
+                Assert.assertNull(properties.get(key));
+            } else {
+                Assert.assertEquals(val, properties.get(key));
+            }
+        }
+    }
+    
+    private int getRandomAspect() {
+        int index = 0;  
+        do {
+            index = _rnd.nextInt(ASPECTS);            
+        } while (_randoms.contains(new Integer(index)));
+        _randoms.add(new Integer(index));
+        return index;
+    }
+
+    // S Service
+    public static interface S {
+        public void invoke();
+    }
+    
+    // S ServiceImpl
+    static class SImpl implements S {
+        public SImpl() {
+        }
+        
+        public String toString() {
+            return "S";
+        }
+        
+        public void invoke() {
+             m_invokeStep.step(ASPECTS+1);
+        }
+    }
+    
+    // S Aspect
+    static class A implements S {
+        private final String m_name;
+        private volatile ServiceRegistration m_registration;
+        private volatile S m_next;
+        private final int m_rank;
+
+        public A(String name, int rank) {
+            m_name = name;
+            m_rank = rank;
+        }
+        
+        public String toString() {
+            return m_name;
+        }
+        
+        public void invoke() {
+            int rank = ServiceUtil.getRanking(m_registration.getReference());
+            m_invokeStep.step(ASPECTS - rank + 1);
+            m_next.invoke();
+        }
+               
+        public void add(ServiceReference ref, S s) {
+            System.out.println("+++ A" + m_rank + ".add:" + s + "/" + ServiceUtil.toString(ref));   
+            m_next = s;
+        }
+
+        public void swap(ServiceReference oldSRef, S oldS, ServiceReference newSRef, S newS) {
+            System.out.println("+++ A" + m_rank + ".swap: new=" + newS + ", props=" + ServiceUtil.toString(newSRef));
+            Assert.assertTrue(m_next == oldS);
+            m_next = newS;
+        }
+
+        public void change(ServiceReference props, S s) {   
+            System.out.println("+++ A" + m_rank + ".change: s=" + s + ", props=" + ServiceUtil.toString(props));
+            if (m_changeStep != null) {
+                int rank = ServiceUtil.getRanking(m_registration.getReference());
+                m_changeStep.step(rank);
+            }
+        }
+        
+        public void remove(ServiceReference props, S s) {
+            System.out.println("+++ A" + m_rank + ".remove: " + s + ", props=" + ServiceUtil.toString(props));
+        }
+    }
+    
+    // Aspect aware client, depending of "S" service aspects.
+    static class Client {
+        private volatile S m_s;
+        private volatile ServiceReference m_sRef;
+
+        public Client() {
+        }
+        
+        public Dictionary getServiceProperties() {
+            Dictionary props = new Hashtable();
+            for (String key : m_sRef.getPropertyKeys()) {
+                props.put(key, m_sRef.getProperty(key));
+            }
+            return props;
+        }
+
+        public void invoke() {
+            m_s.invoke();           
+        }
+
+        public String toString() {
+            return "Client";
+        }
+        
+        public void add(ServiceReference ref, S s) {
+            System.out.println("+++ Client.add: " + s + "/" + ServiceUtil.toString(ref));
+            m_s = s;
+            m_sRef = ref;
+        }
+              
+        public void swap(ServiceReference oldSRef, S oldS, ServiceReference newSRef, S newS) {
+            System.out.println("+++ Client.swap: m_s = " + m_s + ", old=" + oldS + ", oldProps=" + ServiceUtil.toString(oldSRef) + ", new=" + newS + ", props=" + ServiceUtil.toString(newSRef));
+            Assert.assertTrue(m_s == oldS);
+            m_s = newS;
+            m_sRef = newSRef;
+        }
+
+        public void change(ServiceReference properties, S s) {
+            System.out.println("+++ Client.change: s=" + s + ", props=" + ServiceUtil.toString(properties));
+            if (m_changeStep != null) {
+                m_changeStep.step(ASPECTS+1);
+            }
+        }
+        
+        public void remove(ServiceReference props, S s) {
+            System.out.println("+++ Client.remove: " + s + ", props=" + ServiceUtil.toString(props));
+        }
+    }
+    
+    // S2 Service
+    public static interface S2 {
+        public void invoke2();
+    }
+
+    // S2 impl, which adapts S1 interface to S2 interface
+    static class S2Impl implements S2 {
+        private volatile S m_s; // we shall see top-level aspect on S service
+        
+        public void add(ServiceReference ref, S s) {
+            System.out.println("+++ S2Impl.add: " + s + "/" + ServiceUtil.toString(ref));
+            m_s = s;
+        }
+              
+        public void swap(ServiceReference oldSRef, S oldS, ServiceReference newSRef, S newS) {
+            System.out.println("+++ S2Impl.swap: new=" + newS + ", props=" + ServiceUtil.toString(newSRef));
+            m_s = newS;
+        }
+
+        public void change(ServiceReference properties, S s) {
+            System.out.println("+++ S2Impl.change: s=" + s + ", props=" + ServiceUtil.toString(properties));
+            if (m_changeStep != null) {
+                m_changeStep.step(ASPECTS+1);
+            }
+        }
+        
+        public void remove(ServiceReference props, S s) {
+            System.out.println("+++ S2Impl.remove: " + s + ", props=" + ServiceUtil.toString(props));
+        }
+        
+        public void invoke2() {
+            m_s.invoke();
+            m_invokeStep.step(ASPECTS + 2); // All aspects, and S1Impl have been invoked
+        }
+
+        public String toString() {
+            return "S2";
+        }        
+    }
+    
+    // Client2 depending on S2.
+    static class Client2 {
+        private volatile S2 m_s2;
+        private volatile ServiceReference m_s2Ref;
+
+        public Dictionary getServiceProperties() {
+            Dictionary props = new Hashtable();
+            for (String key : m_s2Ref.getPropertyKeys()) {
+                props.put(key, m_s2Ref.getProperty(key));
+            }
+            return props;
+        }
+
+        public void invoke2() {
+            m_s2.invoke2();           
+        }
+
+        public String toString() {
+            return "Client2";
+        }  
+                
+        public void add(ServiceReference ref, S2 s2) {
+            System.out.println("+++ Client2.add: " + s2 + "/" + ServiceUtil.toString(ref));
+            m_s2 = s2;
+            m_s2Ref = ref;
+        }
+
+        public void change(ServiceReference props, S2 s2) {   
+            System.out.println("+++ Client2.change: s2=" + s2 + ", props=" + ServiceUtil.toString(props));
+            if (m_changeStep != null) {
+                m_changeStep.step(ASPECTS + 2); // S1Impl, all aspects, and S2 adapters have been changed before us.
+            }
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/BundleDependencyTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/BundleDependencyTest.java?rev=1564995&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/BundleDependencyTest.java (added)
+++ felix/sandbox/pderop/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/BundleDependencyTest.java Wed Feb  5 23:22:32 2014
@@ -0,0 +1,142 @@
+/*
+ * 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;
+import org.osgi.framework.Bundle;
+
+@RunWith(PaxExam.class)
+public class BundleDependencyTest extends TestBase {
+    @Test
+    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=org.apache.felix.dependencymanager)").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);
+    }
+    
+    @Test
+    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=org.apache.felix.dependencymanager)")
+                .setCallbacks("add", "remove")
+                );
+        // add a consumer with a filter
+        m.add(consumerWithFilter);
+        e.waitForStep(1, 10000);
+        // remove the consumer again
+        m.remove(consumerWithFilter);
+        e.waitForStep(2, 10000);
+    }
+    
+    static class Consumer {
+        private volatile int m_count = 0;
+
+        public void add(Bundle b) {
+            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) {
+            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("org.apache.felix.dependencymanager")) {
+                m_ensure.step(1);
+            }
+        }
+        
+        public void remove(Bundle b) {
+            Assert.assertNotNull(b);
+            if (b.getSymbolicName().equals("org.apache.felix.dependencymanager")) {
+                m_ensure.step(2);
+            }
+        }
+    }
+}



Mime
View raw message