Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/AspectWithPropagationTest.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/AspectWithPropagationTest.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/AspectWithPropagationTest.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/AspectWithPropagationTest.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,716 @@ +/* + * 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.lambda.itest; +import static org.apache.felix.dm.lambda.DependencyManagerActivator.adapter; +import static org.apache.felix.dm.lambda.DependencyManagerActivator.aspect; +import static org.apache.felix.dm.lambda.DependencyManagerActivator.component; + +import java.util.Dictionary; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import org.apache.felix.dm.Component; +import org.apache.felix.dm.DependencyManager; +import org.junit.Assert; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; + + +/** + * Test for aspects with service properties propagations. + * + * @author <a href="mailto:[email protected]">Felix Project Team</a> + */ +@SuppressWarnings({"rawtypes", "unchecked", "unused"}) +public class AspectWithPropagationTest extends TestBase { + private final static int ASPECTS = 3; + private final Set<Integer> _randoms = new HashSet<Integer>(); + private final Random _rnd = new Random(); + private static Ensure m_invokeStep; + private static Ensure m_changeStep; + + /** + * This test does the following: + * + * - Create S service with property "p=s" + * - Create SA (aspect of S) with property "p=aspect" + * - Create Client, depending on S (actually, on SA). + * - Client should see SA with properties p=aspect + * - Change S service property with "p=smodified": the Client should be changed with SA(p=aspect) + * - Change aspect service property with "p=aspectmodified": The client should be changed with SA(p=aspectmodified) + */ + public void testAspectsWithPropagationNotOverriding() { + System.out.println("----------- Running testAspectsWithPropagationNotOverriding ..."); + DependencyManager m = getDM(); + m_invokeStep = new Ensure(); + + // Create our original "S" service. + S s = new S() { + public void invoke() { + } + }; + Component sComp = component(m).impl(s).provides(S.class, p -> "s").build(); + + // Create SA (aspect of S) + S sa = new S() { + volatile S m_s; + public void invoke() { + } + }; + Component saComp = aspect(m, S.class).rank(1).impl(sa).properties(p -> "aspect").build(); + + // Create client depending on S + Object client = new Object() { + int m_changeCount; + void add(Map props, S s) { + Assert.assertEquals("aspect", props.get("p")); + m_invokeStep.step(1); + } + + void change(Map props, S s) { + switch (++m_changeCount) { + case 1: + Assert.assertEquals("aspect", props.get("p")); + m_invokeStep.step(2); + break; + case 2: + Assert.assertEquals("aspectmodified", props.get("p")); + m_invokeStep.step(3); + } + } + }; + Component clientComp = component(m).impl(client).withSrv(S.class, srv->srv.cb("add", "change", null)).build(); + + // Add components in dependency manager + m.add(sComp); + m.add(saComp); + m.add(clientComp); + + // client should have been added with SA aspect + m_invokeStep.waitForStep(1, 5000); + + // now change s "p=s" to "p=smodified": client should not see it + Hashtable props = new Hashtable(); + props.put("p", "smodified"); + sComp.setServiceProperties(props); + m_invokeStep.waitForStep(2, 5000); + + // now change sa aspect "p=aspect" to "p=aspectmodified": client should see it + props = new Hashtable(); + props.put("p", "aspectmodified"); + saComp.setServiceProperties(props); + m_invokeStep.waitForStep(3, 5000); + + // remove components + m.remove(clientComp); + m.remove(saComp); + m.remove(sComp); + } + + /** + * This test does the following: + * + * - Create S service + * - Create some S Aspects + * - Create a Client, depending on S (actually, on the top-level S aspect) + * - Client has a "change" callback in order to track S service properties modifications. + * - First, invoke Client.invoke(): all S aspects, and finally original S service must be invoked orderly. + * - Modify S original service properties, and check if all aspects, and the client has been orderly called in their "change" callback. + * - Modify the First lowest ranked aspect (rank=1), and check if all aspects, and client have been orderly called in their "change" callback. + */ + public void testAspectsWithPropagation() { + System.out.println("----------- Running testAspectsWithPropagation ..."); + DependencyManager m = getDM(); + // helper class that ensures certain steps get executed in sequence + m_invokeStep = new Ensure(); + + // Create our original "S" service. + Dictionary props = new Hashtable(); + props.put("foo", "bar"); + Component s = component(m).impl(new SImpl()).provides(S.class, props).build(); + + // Create an aspect aware client, depending on "S" service. + Client clientImpl; + Component client = component(m).impl((clientImpl = new Client())).withSrv(S.class, srv -> srv.cb("add", "change", "remove", "swap")).build(); + + // Create some "S" aspects + Component[] aspects = new Component[ASPECTS]; + for (int rank = 1; rank <= ASPECTS; rank ++) { + aspects[rank-1] = aspect(m, S.class).rank(rank).impl(new A("A" + rank, rank)).cb("add", "change", "remove", "swap").build(); + props = new Hashtable(); + props.put("a" + rank, "v" + rank); + aspects[rank-1].setServiceProperties(props); + } + + // Register client + m.add(client); + + // Randomly register aspects and original service + boolean originalServiceAdded = false; + for (int i = 0; i < ASPECTS; i ++) { + int index = getRandomAspect(); + m.add(aspects[index]); + if (! originalServiceAdded && _rnd.nextBoolean()) { + m.add(s); + originalServiceAdded = true; + } + } + if (! originalServiceAdded) { + m.add(s); + } + + // All set, check if client has inherited from top level aspect properties + original service properties + Map check = new HashMap(); + check.put("foo", "bar"); + for (int i = 1; i < (ASPECTS - 1); i ++) { + check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect. + } + check.put("a" + ASPECTS, "v" + ASPECTS); + checkServiceProperties(check, clientImpl.getServiceProperties()); + + // Now invoke client, which orderly calls all aspects in the chain, and finally the original service "S". + System.out.println("-------------------------- Invoking client."); + clientImpl.invoke(); + m_invokeStep.waitForStep(ASPECTS+1, 5000); + + // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, and on client. + System.out.println("-------------------------- Modifying original service properties."); + m_changeStep = new Ensure(); + props = new Hashtable(); + props.put("foo", "barModified"); + s.setServiceProperties(props); + + // Check if aspects and client have been orderly called in their "changed" callback + m_changeStep.waitForStep(ASPECTS+1, 5000); + + // Check if modified "foo" original service property has been propagated + check = new HashMap(); + check.put("foo", "barModified"); + for (int i = 1; i < (ASPECTS - 1); i ++) { + check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect. + } + check.put("a" + ASPECTS, "v" + ASPECTS); // we only see top-level aspect service properties + checkServiceProperties(check, clientImpl.getServiceProperties()); + + // Now, change the top-level ranked aspect: it must propagate to all upper aspects, as well as to the client + System.out.println("-------------------------- Modifying top-level aspect service properties."); + + m_changeStep = new Ensure(); + for (int i = 1; i <= ASPECTS; i ++) { + m_changeStep.step(i); // only client has to be changed. + } + props = new Hashtable(); + props.put("a" + ASPECTS, "v" + ASPECTS + "-Modified"); + aspects[ASPECTS-1].setServiceProperties(props); // That triggers change callbacks for upper aspects (with rank >= 2) + m_changeStep.waitForStep(ASPECTS+1, 5000); // check if client have been changed. + + // Check if top level aspect service properties have been propagated up to the client. + check = new HashMap(); + check.put("foo", "barModified"); + for (int i = 1; i < (ASPECTS - 1); i ++) { + check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect. + } + check.put("a" + ASPECTS, "v" + ASPECTS + "-Modified"); + checkServiceProperties(check, clientImpl.getServiceProperties()); + + // Clear all components. + m_changeStep = null; + m.clear(); + } + + /** + * This test does the following: + * + * - Create S service + * - Create some S Aspects without any callbacks (add/change/remove/swap) + * - Create a Client, depending on S (actually, on the top-level S aspect) + * - Client has a "change" callack in order to track S service properties modifications. + * - First, invoke Client.invoke(): all S aspects, and finally original S service must be invoked orderly. + * - Modify S original service properties, and check if the client has been called in its "change" callback. + */ + public void testAspectsWithPropagationAndNoCallbacks() { + System.out.println("----------- Running testAspectsWithPropagation ..."); + DependencyManager m = getDM(); + // helper class that ensures certain steps get executed in sequence + m_invokeStep = new Ensure(); + + // Create our original "S" service. + Dictionary props = new Hashtable(); + props.put("foo", "bar"); + Component s = component(m).impl(new SImpl()).provides(S.class, props).build(); + + // Create an aspect aware client, depending on "S" service. + Client clientImpl; + Component client = component(m).impl((clientImpl = new Client())).withSrv(S.class, srv->srv.cb("add", "change", "remove")).build(); + + // Create some "S" aspects + Component[] aspects = new Component[ASPECTS]; + for (int rank = 1; rank <= ASPECTS; rank ++) { + aspects[rank-1] = aspect(m, S.class).rank(rank).impl(new A("A" + rank, rank)).build(); + props = new Hashtable(); + props.put("a" + rank, "v" + rank); + aspects[rank-1].setServiceProperties(props); + } + + // Register client + m.add(client); + + // Randomly register aspects and original service + boolean originalServiceAdded = false; + for (int i = 0; i < ASPECTS; i ++) { + int index = getRandomAspect(); + m.add(aspects[index]); + if (! originalServiceAdded && _rnd.nextBoolean()) { + m.add(s); + originalServiceAdded = true; + } + } + if (! originalServiceAdded) { + m.add(s); + } + + // All set, check if client has inherited from top level aspect properties + original service properties + Map check = new HashMap(); + check.put("foo", "bar"); + for (int i = 1; i < (ASPECTS - 1); i ++) { + check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect. + } + check.put("a" + ASPECTS, "v" + ASPECTS); + checkServiceProperties(check, clientImpl.getServiceProperties()); + + // Now invoke client, which orderly calls all aspects in the chain, and finally the original service "S". + System.out.println("-------------------------- Invoking client."); + clientImpl.invoke(); + m_invokeStep.waitForStep(ASPECTS+1, 5000); + + // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, and on client. + System.out.println("-------------------------- Modifying original service properties."); + m_changeStep = new Ensure(); + for (int i = 1; i <= ASPECTS; i ++) { + m_changeStep.step(i); // skip aspects, which have no "change" callbacks. + } + props = new Hashtable(); + props.put("foo", "barModified"); + s.setServiceProperties(props); + + // Check if aspects and client have been orderly called in their "changed" callback + m_changeStep.waitForStep(ASPECTS+1, 5000); + + // Check if modified "foo" original service property has been propagated + check = new HashMap(); + check.put("foo", "barModified"); + for (int i = 1; i < (ASPECTS - 1); i ++) { + check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect. + } + check.put("a" + ASPECTS, "v" + ASPECTS); // we only see top-level aspect service properties + checkServiceProperties(check, clientImpl.getServiceProperties()); + + // Clear all components. + m_changeStep = null; + m.clear(); + } + + /** + * This test does the following: + * + * - Create S service + * - Create some S Aspects + * - Create S2 Adapter, which adapts S to S2 + * - Create Client2, which depends on S2. Client2 listens to S2 property change events. + * - Now, invoke Client2.invoke(): all S aspects, and finally original S service must be invoked orderly. + * - Modify S original service properties, and check if all aspects, S2 Adapter, and Client2 have been orderly called in their "change" callback. + */ + public void testAdapterWithAspectsAndPropagation() { + System.out.println("----------- Running testAdapterWithAspectsAndPropagation ..."); + + DependencyManager m = getDM(); + m_invokeStep = new Ensure(); + + // Create our original "S" service. + Dictionary props = new Hashtable(); + props.put("foo", "bar"); + Component s = component(m).impl(new SImpl()).provides(S.class, props).build(); + + // Create some "S" aspects + Component[] aspects = new Component[ASPECTS]; + for (int rank = 1; rank <= ASPECTS; rank ++) { + aspects[rank-1] = aspect(m, S.class).rank(rank).impl(new A("A" + rank, rank)).cb("add", "change", "remove", "swap").build(); + 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 = adapter(m, S.class).cb("add", "change", "remove", "swap").provides(S2.class).impl(new S2Impl()).build(); + + // Create Client2, which depends on "S2" service. + Client2 client2Impl; + Component client2 = component(m).impl((client2Impl = new Client2())).withSrv(S2.class, srv -> srv.cb("add", "change", null)).build(); + + // Register client2 + m.add(client2); + + // Register S2 adapter + m.add(adapter); + + // Randomly register aspects, original service + boolean originalServiceAdded = false; + for (int i = 0; i < ASPECTS; i ++) { + int index = getRandomAspect(); + m.add(aspects[index]); + if (! originalServiceAdded && _rnd.nextBoolean()) { + m.add(s); + originalServiceAdded = true; + } + } + if (! originalServiceAdded) { + m.add(s); + } + + // Now invoke client2, which orderly calls all S1 aspects, then S1Impl, and finally S2 service + System.out.println("-------------------------- Invoking client2."); + client2Impl.invoke2(); + m_invokeStep.waitForStep(ASPECTS+2, 5000); + + // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, S2Impl, and Client2. + System.out.println("-------------------------- Modifying original service properties."); + m_changeStep = new Ensure(); + props = new Hashtable(); + props.put("foo", "barModified"); + s.setServiceProperties(props); + + // Check if aspects and Client2 have been orderly called in their "changed" callback + m_changeStep.waitForStep(ASPECTS+2, 5000); + + // Check if modified "foo" original service property has been propagated to Client2 + Map check = new HashMap(); + check.put("foo", "barModified"); + for (int i = 1; i < (ASPECTS - 1); i ++) { + check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect. + } + check.put("a" + ASPECTS, "v" + ASPECTS); + checkServiceProperties(check, client2Impl.getServiceProperties()); + + // Clear all components. + m_changeStep = null; + m.clear(); + } + + /** + * This test does the following: + * + * - Create S service + * - Create some S Aspects without any callbacks (add/change/remove) + * - Create S2 Adapter, which adapts S to S2 (but does not have any add/change/remove callbacks) + * - Create Client2, which depends on S2. Client2 listens to S2 property change events. + * - Now, invoke Client2.invoke(): all S aspects, and finally original S service must be invoked orderly. + * - Modify S original service properties, and check if all aspects, S2 Adapter, and Client2 have been orderly called in their "change" callback. + */ + public void testAdapterWithAspectsAndPropagationNoCallbacks() { + System.out.println("----------- Running testAdapterWithAspectsAndPropagationNoCallbacks ..."); + + DependencyManager m = getDM(); + m_invokeStep = new Ensure(); + + // Create our original "S" service. + Dictionary props = new Hashtable(); + props.put("foo", "bar"); + Component s = component(m).impl(new SImpl()).provides(S.class, props).build(); + + // Create some "S" aspects + Component[] aspects = new Component[ASPECTS]; + for (int rank = 1; rank <= ASPECTS; rank ++) { + aspects[rank-1] = aspect(m, S.class).rank(rank).impl(new A("A" + rank, rank)).build(); + 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 = adapter(m, S.class).provides(S2.class).impl(new S2Impl()).build(); + + // Create Client2, which depends on "S2" service. + Client2 client2Impl; + Component client2 = component(m).impl((client2Impl = new Client2())).withSrv(S2.class, srv->srv.cb("add", "change", null)).build(); + + // Register client2 + m.add(client2); + + // Register S2 adapter + m.add(adapter); + + // Randomly register aspects, original service + boolean originalServiceAdded = false; + for (int i = 0; i < ASPECTS; i ++) { + int index = getRandomAspect(); + m.add(aspects[index]); + if (! originalServiceAdded && _rnd.nextBoolean()) { + m.add(s); + originalServiceAdded = true; + } + } + if (! originalServiceAdded) { + m.add(s); + } + + // Now invoke client2, which orderly calls all S1 aspects, then S1Impl, and finally S2 service + System.out.println("-------------------------- Invoking client2."); + client2Impl.invoke2(); + m_invokeStep.waitForStep(ASPECTS+2, 5000); + + // Now, change original service "S" properties: this will orderly trigger "change" callbacks on aspects, S2Impl, and Client2. + System.out.println("-------------------------- Modifying original service properties."); + m_changeStep = new Ensure(); + for (int i = 1; i <= ASPECTS+1; i ++) { + m_changeStep.step(i); // skip all aspects and the adapter + } + props = new Hashtable(); + props.put("foo", "barModified"); + s.setServiceProperties(props); + + // Check if Client2 has been called in its "changed" callback + m_changeStep.waitForStep(ASPECTS+2, 5000); + + // Check if modified "foo" original service property has been propagated to Client2 + Map check = new HashMap(); + check.put("foo", "barModified"); + for (int i = 1; i < (ASPECTS - 1); i ++) { + check.put("a" + i, null); // we must not inherit from lower ranks, only from the top-level aspect. + } + check.put("a" + ASPECTS, "v" + ASPECTS); + checkServiceProperties(check, client2Impl.getServiceProperties()); + + // Clear all components. + m_changeStep = null; + m.clear(); + } + + private void checkServiceProperties(Map<?, ?> check, Dictionary properties) { + for (Object key : check.keySet()) { + Object val = check.get(key); + if (val == null) { + Assert.assertNull(properties.get(key)); + } else { + Assert.assertEquals(val, properties.get(key)); + } + } + } + + private int getRandomAspect() { + int index = 0; + do { + index = _rnd.nextInt(ASPECTS); + } while (_randoms.contains(new Integer(index))); + _randoms.add(new Integer(index)); + return index; + } + + // S Service + public static interface S { + public void invoke(); + } + + // S ServiceImpl + static class SImpl implements S { + public SImpl() { + } + + public String toString() { + return "S"; + } + + public void invoke() { + m_invokeStep.step(ASPECTS+1); + } + } + + // S Aspect + static class A implements S { + private final String m_name; + private volatile ServiceRegistration m_registration; + private volatile S m_next; + private final int m_rank; + + public A(String name, int rank) { + m_name = name; + m_rank = rank; + } + + public String toString() { + return m_name; + } + + public void invoke() { + int rank = ServiceUtil.getRanking(m_registration.getReference()); + m_invokeStep.step(ASPECTS - rank + 1); + m_next.invoke(); + } + + public void add(ServiceReference ref, S s) { + System.out.println("+++ A" + m_rank + ".add:" + s + "/" + ServiceUtil.toString(ref)); + m_next = s; + } + + public void swap(ServiceReference oldSRef, S oldS, ServiceReference newSRef, S newS) { + System.out.println("+++ A" + m_rank + ".swap: new=" + newS + ", props=" + ServiceUtil.toString(newSRef)); + Assert.assertTrue(m_next == oldS); + m_next = newS; + } + + public void change(ServiceReference props, S s) { + System.out.println("+++ A" + m_rank + ".change: s=" + s + ", props=" + ServiceUtil.toString(props)); + if (m_changeStep != null) { + int rank = ServiceUtil.getRanking(m_registration.getReference()); + m_changeStep.step(rank); + } + } + + public void remove(ServiceReference props, S s) { + System.out.println("+++ A" + m_rank + ".remove: " + s + ", props=" + ServiceUtil.toString(props)); + } + } + + // Aspect aware client, depending of "S" service aspects. + static class Client { + private volatile S m_s; + private volatile ServiceReference m_sRef; + + public Client() { + } + + public Dictionary getServiceProperties() { + Dictionary props = new Hashtable(); + for (String key : m_sRef.getPropertyKeys()) { + props.put(key, m_sRef.getProperty(key)); + } + return props; + } + + public void invoke() { + m_s.invoke(); + } + + public String toString() { + return "Client"; + } + + public void add(ServiceReference ref, S s) { + System.out.println("+++ Client.add: " + s + "/" + ServiceUtil.toString(ref)); + m_s = s; + m_sRef = ref; + } + + public void swap(ServiceReference oldSRef, S oldS, ServiceReference newSRef, S newS) { + System.out.println("+++ Client.swap: m_s = " + m_s + ", old=" + oldS + ", oldProps=" + ServiceUtil.toString(oldSRef) + ", new=" + newS + ", props=" + ServiceUtil.toString(newSRef)); + Assert.assertTrue(m_s == oldS); + m_s = newS; + m_sRef = newSRef; + } + + public void change(ServiceReference properties, S s) { + System.out.println("+++ Client.change: s=" + s + ", props=" + ServiceUtil.toString(properties)); + if (m_changeStep != null) { + m_changeStep.step(ASPECTS+1); + } + } + + public void remove(ServiceReference props, S s) { + System.out.println("+++ Client.remove: " + s + ", props=" + ServiceUtil.toString(props)); + } + } + + // S2 Service + public static interface S2 { + public void invoke2(); + } + + // S2 impl, which adapts S1 interface to S2 interface + static class S2Impl implements S2 { + private volatile S m_s; // we shall see top-level aspect on S service + + public void add(ServiceReference ref, S s) { + System.out.println("+++ S2Impl.add: " + s + "/" + ServiceUtil.toString(ref)); + m_s = s; + } + + public void swap(ServiceReference oldSRef, S oldS, ServiceReference newSRef, S newS) { + System.out.println("+++ S2Impl.swap: new=" + newS + ", props=" + ServiceUtil.toString(newSRef)); + m_s = newS; + } + + public void change(ServiceReference properties, S s) { + System.out.println("+++ S2Impl.change: s=" + s + ", props=" + ServiceUtil.toString(properties)); + if (m_changeStep != null) { + m_changeStep.step(ASPECTS+1); + } + } + + public void remove(ServiceReference props, S s) { + System.out.println("+++ S2Impl.remove: " + s + ", props=" + ServiceUtil.toString(props)); + } + + public void invoke2() { + m_s.invoke(); + m_invokeStep.step(ASPECTS + 2); // All aspects, and S1Impl have been invoked + } + + public String toString() { + return "S2"; + } + } + + // Client2 depending on S2. + static class Client2 { + private volatile S2 m_s2; + private volatile ServiceReference m_s2Ref; + + public Dictionary getServiceProperties() { + Dictionary props = new Hashtable(); + for (String key : m_s2Ref.getPropertyKeys()) { + props.put(key, m_s2Ref.getProperty(key)); + } + return props; + } + + public void invoke2() { + m_s2.invoke2(); + } + + public String toString() { + return "Client2"; + } + + public void add(ServiceReference ref, S2 s2) { + System.out.println("+++ Client2.add: " + s2 + "/" + ServiceUtil.toString(ref)); + m_s2 = s2; + m_s2Ref = ref; + } + + public void change(ServiceReference props, S2 s2) { + System.out.println("+++ Client2.change: s2=" + s2 + ", props=" + ServiceUtil.toString(props)); + if (m_changeStep != null) { + m_changeStep.step(ASPECTS + 2); // S1Impl, all aspects, and S2 adapters have been changed before us. + } + } + } +}
Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/AutoConfigTest.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/AutoConfigTest.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/AutoConfigTest.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/AutoConfigTest.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,251 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.felix.dm.lambda.itest; + +import static org.apache.felix.dm.lambda.DependencyManagerActivator.component; + +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.apache.felix.dm.Component; +import org.apache.felix.dm.DependencyManager; +import org.junit.Assert; +import org.osgi.framework.Constants; + +/** + * @author <a href="mailto:[email protected]">Felix Project Team</a> + */ +@SuppressWarnings({"unchecked", "rawtypes"}) +public class AutoConfigTest extends TestBase { + private final Ensure m_ensure = new Ensure(); + + public void testField() throws Exception { + final DependencyManager dm = getDM(); + // Create a consumer, depending on some providers (autoconfig field). + ConsumeWithProviderField consumer = new ConsumeWithProviderField(); + Component c = createConsumer(dm, consumer); + // Create two providers + Component p1 = createProvider(dm, 10, new Provider() { + public String toString() { return "provider1"; } + public void run() { m_ensure.step(); } + }); + Component p2 = createProvider(dm, 20, new Provider() { + public String toString() { return "provider2"; } + public void run() { m_ensure.step(); } + }); + + // add the two providers + dm.add(p2); + dm.add(p1); + // add the consumer, which should have been injected with provider2 (highest rank) + dm.add(c); + m_ensure.waitForStep(1, 5000); + // remove the provider2, the consumer should now be injected with provider1 + dm.remove(p2); + Assert.assertNotNull(consumer.getProvider()); + Assert.assertEquals("provider1", consumer.getProvider().toString()); + // remove the provider1, the consumer should have been stopped + dm.remove(p1); + m_ensure.waitForStep(2, 5000); + dm.clear(); + } + + public void testIterableField() throws Exception { + final DependencyManager dm = getDM(); + ConsumerWithIterableField consumer = new ConsumerWithIterableField(); + Component c = createConsumer(dm, consumer); + Component p1 = createProvider(dm, 10, new Provider() { + public void run() { m_ensure.step(); } + public String toString() { return "provider1"; } + }); + Component p2 = createProvider(dm, 20, new Provider() { + public void run() { m_ensure.step();} + public String toString() { return "provider2"; } + }); + + dm.add(p2); + dm.add(p1); + dm.add(c); + // the consumer should have been injected with all providers. + m_ensure.waitForStep(3, 5000); + + // check if all providers are there + Assert.assertNotNull(consumer.getProvider("provider1")); + Assert.assertNotNull(consumer.getProvider("provider2")); + + // remove provider1 + dm.remove(p1); + + // check if provider1 has been removed and if provider2 is still there + Assert.assertNull(consumer.getProvider("provider1")); + Assert.assertNotNull(consumer.getProvider("provider2")); + + // remove provider2, the consumer should be stopped + dm.remove(p2); + m_ensure.waitForStep(4, 5000); + dm.clear(); + } + + public void testMapField() throws Exception { + final DependencyManager dm = getDM(); + ConsumerWithMapField consumer = new ConsumerWithMapField(); + Component c = createConsumer(dm, consumer); + Component p1 = createProvider(dm, 10, new Provider() { + public void run() { m_ensure.step(); } + public String toString() { return "provider1"; } + }); + Component p2 = createProvider(dm, 20, new Provider() { + public void run() { m_ensure.step();} + public String toString() { return "provider2"; } + }); + + dm.add(p2); + dm.add(p1); + dm.add(c); + // the consumer should have been injected with all providers. + m_ensure.waitForStep(3, 5000); + + // check if all providers are there + Assert.assertNotNull(consumer.getProvider("provider1")); + Assert.assertNotNull(consumer.getProvider("provider2")); + + // remove provider1 + dm.remove(p1); + + // check if provider1 has been removed and if provider2 is still there + Assert.assertNull(consumer.getProvider("provider1")); + Assert.assertNotNull(consumer.getProvider("provider2")); + + // remove provider2, the consumer should be stopped + dm.remove(p2); + m_ensure.waitForStep(4, 5000); + dm.clear(); + } + + private Component createProvider(DependencyManager dm, int rank, Provider provider) { + return component(dm).impl(provider).provides(Provider.class, Constants.SERVICE_RANKING, new Integer(rank)).build(); + } + + private Component createConsumer(DependencyManager dm, Object consumer) { + return component(dm).impl(consumer).withSrv(Provider.class).build(); + } + + public static interface Provider extends Runnable { + } + + public class ConsumeWithProviderField { + volatile Provider m_provider; + + void start() { + Assert.assertNotNull(m_provider); + Assert.assertEquals("provider2", m_provider.toString()); + m_ensure.step(1); + } + + public Provider getProvider() { + return m_provider; + } + + void stop() { + m_ensure.step(2); + } + } + + public class ConsumerWithIterableField { + final Iterable<Provider> m_providers = new ConcurrentLinkedQueue<>(); + final List m_notInjectMe = new ArrayList(); + + void start() { + Assert.assertNotNull(m_providers); + int found = 0; + for (Provider provider : m_providers) { + provider.run(); + found ++; + } + Assert.assertTrue(found == 2); + // The "m_notInjectMe" should not be injected with anything + Assert.assertEquals(m_notInjectMe.size(), 0); + m_ensure.step(3); + } + + public Provider getProvider(String name) { + System.out.println("getProvider(" + name + ") : proviers=" + m_providers); + for (Provider provider : m_providers) { + if (provider.toString().equals(name)) { + return provider; + } + } + return null; + } + + void stop() { + m_ensure.step(4); + } + } + + public class ConsumerWithMapField { + final Map<Provider, Dictionary> m_providers = new ConcurrentHashMap<>(); + final Map m_notInjectMe = new HashMap<>(); + + void start() { + Assert.assertNotNull(m_providers); + System.out.println("ConsumerMap.start: injected providers=" + m_providers); + Assert.assertTrue(m_providers.size() == 2); + Assert.assertEquals(0, m_notInjectMe.size()); + for (Map.Entry<Provider, Dictionary> e : m_providers.entrySet()) { + Provider provider = e.getKey(); + Dictionary props = e.getValue(); + + provider.run(); + if (provider.toString().equals("provider1")) { + Assert.assertEquals(props.get(Constants.SERVICE_RANKING), 10); + } else if (provider.toString().equals("provider2")) { + Assert.assertEquals(props.get(Constants.SERVICE_RANKING), 20); + } else { + Assert.fail("Did not find any properties for provider " + provider); + } + } + + m_ensure.step(3); + } + + public Provider getProvider(String name) { + System.out.println("getProvider(" + name + ") : providers=" + m_providers); + for (Provider provider : m_providers.keySet()) { + if (provider.toString().equals(name)) { + return provider; + } + } + return null; + } + + Map<Provider, Dictionary> getProviders() { + return m_providers; + } + + void stop() { + m_ensure.step(4); + } + } +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/BundleAdapterTest.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/BundleAdapterTest.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/BundleAdapterTest.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/BundleAdapterTest.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,187 @@ +/* + * 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.lambda.itest; + +import static org.apache.felix.dm.lambda.DependencyManagerActivator.bundleAdapter; +import static org.apache.felix.dm.lambda.DependencyManagerActivator.component; + +import org.apache.felix.dm.Component; +import org.apache.felix.dm.DependencyManager; +import org.junit.Assert; +import org.osgi.framework.Bundle; + +/** + * @author <a href="mailto:[email protected]">Felix Project Team</a> + */ +public class BundleAdapterTest extends TestBase { + public void testBundleAdapter() { + DependencyManager m = getDM(); + // create a bundle adapter service (one is created for each bundle) + Component adapter = bundleAdapter(m) + .mask(Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE) + .impl(BundleAdapter.class) + .provides(BundleAdapter.class) + .build(); + + // create a service provider and consumer + Consumer c = new Consumer(); + Component consumer = m.createComponent().setImplementation(c) + .add(m.createServiceDependency().setService(BundleAdapter.class).setCallbacks("add", "remove")); + + // add the bundle adapter + m.add(adapter); + // add the service consumer + m.add(consumer); + // check if at least one bundle was found + c.check(); + // remove the consumer again + m.remove(consumer); + // check if all bundles were removed correctly + c.doubleCheck(); + // remove the bundle adapter + m.remove(adapter); + } + + public void testBundleAdapterWithCallbackInstance() { + DependencyManager m = getDM(); + // create a bundle adapter service (one is created for each bundle) + BundleAdapterWithCallback baWithCb = new BundleAdapterWithCallback(); + BundleAdapterCallbackInstance cbInstance = new BundleAdapterCallbackInstance(baWithCb); + + Component adapter = bundleAdapter(m) + .mask(Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE) + .cbi(cbInstance, "add", "remove") + .impl(baWithCb) + .provides(BundleAdapter.class.getName()) + .build(); + + // create a service provider and consumer + Consumer c = new Consumer(); + Component consumer = component(m) + .impl(c) + .withSrv(BundleAdapter.class, s->s.cb("add", "remove")) + .build(); + + // add the bundle adapter + m.add(adapter); + // add the service consumer + m.add(consumer); + // check if at least one bundle was found + c.check(); + // remove the consumer again + m.remove(consumer); + // check if all bundles were removed correctly + c.doubleCheck(); + // remove the bundle adapter + m.remove(adapter); + } + + public void testBundleAdapterWithCallbackInstanceRef() { + DependencyManager m = getDM(); + // create a bundle adapter service (one is created for each bundle) + BundleAdapterWithCallback baWithCb = new BundleAdapterWithCallback(); + BundleAdapterCallbackInstance cbInstance = new BundleAdapterCallbackInstance(baWithCb); + + Component adapter = bundleAdapter(m) + .mask(Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE) + .cbi(cbInstance::add, cbInstance::remove) + .impl(baWithCb) + .provides(BundleAdapter.class.getName()) + .build(); + + // create a service provider and consumer + Consumer c = new Consumer(); + Component consumer = component(m) + .impl(c) + .withSrv(BundleAdapter.class, s->s.cb("add", "remove")) + .build(); + + // add the bundle adapter + m.add(adapter); + // add the service consumer + m.add(consumer); + // check if at least one bundle was found + c.check(); + // remove the consumer again + m.remove(consumer); + // check if all bundles were removed correctly + c.doubleCheck(); + // remove the bundle adapter + m.remove(adapter); + } + + public static class BundleAdapter { + volatile Bundle m_bundle; + + Bundle getBundle() { + return m_bundle; + } + } + + public static class BundleAdapterWithCallback extends BundleAdapter { + void add(Bundle b) { + m_bundle = b; + } + + void remove(Bundle b) { + m_bundle = null; + } + } + + public static class BundleAdapterCallbackInstance { + final BundleAdapterWithCallback m_ba; + + BundleAdapterCallbackInstance(BundleAdapterWithCallback ba) { + m_ba = ba; + } + + void add(Component c, Bundle b) { + m_ba.add(b); + } + + void remove(Component c, Bundle b) { + m_ba.remove(b); + } + } + + static class Consumer { + private volatile int m_count = 0; + + public void add(BundleAdapter ba) { + Bundle b = ba.getBundle(); + System.out.println("Consumer.add(" + b.getSymbolicName() + ")"); + Assert.assertNotNull("bundle instance must not be null", b); + m_count++; + } + + public void check() { + Assert.assertTrue("we should have found at least one bundle", m_count > 0); + } + + public void remove(BundleAdapter ba) { + Bundle b = ba.getBundle(); + System.out.println("Consumer.remove(" + b.getSymbolicName() + ")"); + m_count--; + } + + public void doubleCheck() { + Assert.assertEquals("all bundles we found should have been removed again", 0, m_count); + } + } +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/BundleAdapterWithCallbacksNotAutoConfiguredTest.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/BundleAdapterWithCallbacksNotAutoConfiguredTest.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/BundleAdapterWithCallbacksNotAutoConfiguredTest.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/BundleAdapterWithCallbacksNotAutoConfiguredTest.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,67 @@ +/* + * 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.lambda.itest; + +import static org.apache.felix.dm.lambda.DependencyManagerActivator.bundleAdapter; + +import org.apache.felix.dm.Component; +import org.apache.felix.dm.DependencyManager; +import org.junit.Assert; +import org.osgi.framework.Bundle; + +/** + * @author <a href="mailto:[email protected]">Felix Project Team</a> + */ +public class BundleAdapterWithCallbacksNotAutoConfiguredTest extends TestBase { + final Ensure m_e = new Ensure(); + + public void testBundleAdapterWithCallbacksNotAutoConfigured() { + DependencyManager m = getDM(); + // create a bundle adapter service (one is created for each bundle) + BundleAdapterWithCallback baWithCb = new BundleAdapterWithCallback(); + String bsn = "org.apache.felix.dependencymanager"; + String filter = "(Bundle-SymbolicName=" + bsn + ")"; + + Component adapter = bundleAdapter(m).mask(Bundle.ACTIVE).filter(filter).cb("add").impl(baWithCb).build(); + + // add the bundle adapter + m.add(adapter); + + // Check if adapter has not been auto configured (because it has callbacks defined). + m_e.waitForStep(1, 3000); + Assert.assertNull("bundle adapter must not be auto configured", baWithCb.getBundle()); + + // remove the bundle adapters + m.remove(adapter); + } + + class BundleAdapterWithCallback { + volatile Bundle m_bundle; // must not be auto configured because we are using callbacks. + + Bundle getBundle() { + return m_bundle; + } + + void add(Bundle b) { + Assert.assertNotNull(b); + Assert.assertEquals("org.apache.felix.dependencymanager", b.getSymbolicName()); + m_e.step(1); + } + } +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/BundleDependencyTest.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/BundleDependencyTest.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/BundleDependencyTest.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/BundleDependencyTest.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.felix.dm.lambda.itest; + +import static org.apache.felix.dm.lambda.DependencyManagerActivator.component; + +import org.apache.felix.dm.Component; +import org.apache.felix.dm.DependencyManager; +import org.junit.Assert; +import org.osgi.framework.Bundle; + +/** + * @author <a href="mailto:[email protected]">Felix Project Team</a> + */ +public class BundleDependencyTest extends TestBase { + private final static String BSN = "org.apache.felix.metatype"; + + public void testBundleDependencies() { + DependencyManager m = getDM(); + // create a service provider and consumer + MyConsumer c = new MyConsumer(); + Component consumer = component(m, comp -> comp.impl(c).withBundle(bundle -> bundle.cb("add", "remove"))); + + // 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(); + String filter = "(Bundle-SymbolicName=" + BSN + ")"; + Component consumerWithFilter = component(m, comp -> comp.impl(new FilteredConsumer(e)) + .withBundle(bundle-> bundle.filter(filter).cb("add", "remove"))); + e.step(2); + // remove the consumer again + m.remove(consumerWithFilter); + e.step(4); + } + + public void testBundleDependenciesRef() { + DependencyManager m = getDM(); + // create a service provider and consumer + MyConsumer c = new MyConsumer(); + Component consumer = component(m, comp -> comp.impl(c).withBundle(bundle -> bundle.cb(MyConsumer::add, MyConsumer::remove))); + + // 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(); + String filter = "(Bundle-SymbolicName=" + BSN + ")"; + Component consumerWithFilter = component(m, comp -> comp.impl(new FilteredConsumer(e)) + .withBundle(bundle-> bundle.filter(filter).cb(FilteredConsumer::add, FilteredConsumer::remove))); + e.step(2); + // remove the consumer again + m.remove(consumerWithFilter); + e.step(4); + } + + public void testRequiredBundleDependency() { + DependencyManager m = getDM(); + + // helper class that ensures certain steps get executed in sequence + Ensure e = new Ensure(); + Component consumerWithFilter = component(m, c -> c.impl(new FilteredConsumerRequired(e)) + .withBundle(b -> b.filter("(Bundle-SymbolicName=" + BSN + ")").cb("add", "remove"))); + e.waitForStep(1, 5000); + // remove the consumer again + m.remove(consumerWithFilter); + e.waitForStep(2, 5000); + } + + public void testRequiredBundleDependencyRef() { + DependencyManager m = getDM(); + + // helper class that ensures certain steps get executed in sequence + Ensure e = new Ensure(); + FilteredConsumerRequired impl = new FilteredConsumerRequired(e); + Component consumerWithFilter = component(m, c -> c.impl(impl) + .withBundle(b -> b.filter("(Bundle-SymbolicName=" + BSN + ")").cbi(impl::add, impl::remove))); + e.waitForStep(1, 5000); + // remove the consumer again + m.remove(consumerWithFilter); + e.waitForStep(2, 5000); + } + + public void testRequiredBundleDependencyWithComponentArgInCallbackMethod() { + DependencyManager m = getDM(); + + // helper class that ensures certain steps get executed in sequence + Ensure e = new Ensure(); + // add a consumer with a filter + FilteredConsumerRequiredWithComponentArg impl = new FilteredConsumerRequiredWithComponentArg(e); + Component consumerWithFilter = component(m, c -> c.impl(impl) + .withBundle(b -> b.filter("(Bundle-SymbolicName=" + BSN + ")").cb("add", "remove"))); + e.waitForStep(1, 5000); + // remove the consumer again + m.remove(consumerWithFilter); + e.waitForStep(2, 5000); + } + + public void testRequiredBundleDependencyWithComponentArgInCallbackMethodRef() { + DependencyManager m = getDM(); + + // helper class that ensures certain steps get executed in sequence + Ensure e = new Ensure(); + FilteredConsumerRequiredWithComponentArg impl = new FilteredConsumerRequiredWithComponentArg(e); + Component consumerWithFilter = component(m).impl(impl) + .withBundle(b -> b.filter("(Bundle-SymbolicName=" + BSN + ")").cbi(impl::add, impl::remove)).build(); + // add a consumer with a filter + m.add(consumerWithFilter); + e.waitForStep(1, 5000); + // remove the consumer again + m.remove(consumerWithFilter); + e.waitForStep(2, 5000); + } + + static class MyConsumer { + private volatile int m_count = 0; + + public void add(Bundle b) { + System.out.println("Consumer.add(" + b.getSymbolicName() + ")"); + Assert.assertNotNull("bundle instance must not be null", b); + m_count++; + } + + public void check() { + Assert.assertTrue("we should have found at least one bundle", m_count > 0); + } + + public void remove(Bundle b) { + System.out.println("Consumer.remove(" + b.getSymbolicName() + ")"); + m_count--; + } + + public void doubleCheck() { + Assert.assertEquals("all bundles we found should have been removed again", 0, m_count); + } + } + + static class FilteredConsumer { + private final Ensure m_ensure; + + public FilteredConsumer(Ensure e) { + m_ensure = e; + } + + public void add(Bundle b) { + m_ensure.step(1); + } + + public void remove(Bundle b) { + m_ensure.step(3); + } + } + + static class FilteredConsumerRequired { + private final Ensure m_ensure; + + public FilteredConsumerRequired(Ensure e) { + m_ensure = e; + } + + public void add(Bundle b) { + System.out.println("Bundle is " + b); +// Assert.assertNotNull(b); + if (b.getSymbolicName().equals(BSN)) { + m_ensure.step(1); + } + } + + public void remove(Bundle b) { + Assert.assertNotNull(b); + if (b.getSymbolicName().equals(BSN)) { + m_ensure.step(2); + } + } + } + + static class FilteredConsumerRequiredWithComponentArg { + private final Ensure m_ensure; + + public FilteredConsumerRequiredWithComponentArg(Ensure e) { + m_ensure = e; + } + + public void add(Component component, Bundle b) { + Assert.assertNotNull(component); + if (b.getSymbolicName().equals(BSN)) { + m_ensure.step(1); + } + } + + public void remove(Component component, Bundle b) { + Assert.assertNotNull(component); + Assert.assertNotNull(b); + if (b.getSymbolicName().equals(BSN)) { + m_ensure.step(2); + } + } + } +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/ComponentTest.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/ComponentTest.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/ComponentTest.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/ComponentTest.java Sun Jan 31 23:27:05 2016 @@ -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.lambda.itest; + +import static org.apache.felix.dm.lambda.DependencyManagerActivator.component; + +import java.util.Dictionary; + +import org.apache.felix.dm.DependencyManager; +import org.junit.Assert; + +/** + * @author <a href="mailto:[email protected]">Felix Project Team</a> + */ +public class ComponentTest extends TestBase { + private final Ensure m_ensure = new Ensure(); + + public void testSimple() throws Exception { + final DependencyManager dm = getDM(); + + // Create consumer (dependency is required by default using builder api). + component(dm, comp -> comp + .factory(Consumer::new) + .withSrv(Provider.class, srv -> srv.filter("(name=provider2)").cb(Consumer::add, Consumer::remove)) + .withSrv(Provider.class, srv -> srv.filter("(name=provider1)").autoConfig("m_autoConfiguredProvider"))); + + // Create providers (auto added to dependency manager) + component(dm, comp -> comp + .impl(new Provider() { public String toString() { return "provider1";}}) + .provides(Provider.class).properties("name", "provider1")); + + component(dm, comp -> comp + .impl(new Provider() { public String toString() { return "provider2";}}) + .provides(Provider.class).properties("name", "provider2")); + + m_ensure.waitForStep(2, 5000); + dm.clear(); + m_ensure.waitForStep(5, 5000); + } + + public static interface Provider { + } + + public class Consumer { + Provider m_provider; + Provider m_autoConfiguredProvider; + + void add(Provider provider, Dictionary<String, Object> props) { + Assert.assertNotNull(provider); + Assert.assertEquals("provider2", props.get("name")); + m_provider = provider; + m_ensure.step(1); + } + + void start() { + Assert.assertNotNull(m_autoConfiguredProvider); + Assert.assertEquals("provider1", m_autoConfiguredProvider.toString()); + m_ensure.step(2); + } + + void stop() { + m_ensure.step(3); + } + + void destroy() { + m_ensure.step(4); + } + + void remove(Provider provider, Dictionary<String, Object> props) { + Assert.assertEquals(m_provider, provider); + m_ensure.step(5); + } + } +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/CompositionTest.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/CompositionTest.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/CompositionTest.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/CompositionTest.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,123 @@ +/* + * 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.lambda.itest; + +import static org.apache.felix.dm.lambda.DependencyManagerActivator.component; + +import org.apache.felix.dm.Component; +import org.apache.felix.dm.DependencyManager; + + +/** + * @author <a href="mailto:[email protected]">Felix Project Team</a> + */ +public class CompositionTest extends TestBase { + public void testComposition() { + DependencyManager m = getDM(); + // helper class that ensures certain steps get executed in sequence + Ensure e = new Ensure(); + // create a service provider and consumer + Component sp = component(m).impl(new ServiceProvider(e)).provides(ServiceInterface.class).build(); + Component sc = component(m).impl(new ServiceConsumer(e)).composition("getComposition") + .withSrv(ServiceInterface.class, sb->sb.cb("add")).build(); + m.add(sp); + m.add(sc); + // ensure we executed all steps inside the component instance + e.step(6); + m.clear(); + } + + public void testCompositionRef() { + DependencyManager m = getDM(); + // helper class that ensures certain steps get executed in sequence + Ensure e = new Ensure(); + // create a service provider and consumer + Component sp = component(m).impl(new ServiceProvider(e)).provides(ServiceInterface.class).build(); + ServiceConsumer scimpl = new ServiceConsumer(e); + Component sc = component(m).impl(scimpl).composition(scimpl::getComposition) + .withSrv(ServiceInterface.class, sb->sb.cb("add")).build(); + m.add(sp); + m.add(sc); + // ensure we executed all steps inside the component instance + e.step(6); + m.clear(); + } + + static interface ServiceInterface { + public void invoke(); + } + + static class ServiceProvider implements ServiceInterface { + private final Ensure m_ensure; + public ServiceProvider(Ensure e) { + m_ensure = e; + } + public void invoke() { + m_ensure.step(4); + } + } + + static class ServiceConsumer { + private final Ensure m_ensure; + private ServiceConsumerComposite m_composite; + @SuppressWarnings("unused") + private ServiceInterface m_service; + + public ServiceConsumer(Ensure e) { + m_ensure = e; + m_composite = new ServiceConsumerComposite(m_ensure); + } + + public Object[] getComposition() { + return new Object[] { this, m_composite }; + } + + void add(ServiceInterface service) { + m_ensure.step(1); + m_service = service; // This method seems to not being called anymore + } + + void start() { + m_composite.invoke(); + m_ensure.step(5); + } + } + + static class ServiceConsumerComposite { + ServiceInterface m_service; + private Ensure m_ensure; + + ServiceConsumerComposite(Ensure ensure) + { + m_ensure = ensure; + } + + void add(ServiceInterface service) { + + m_ensure.step(2); + m_service = service; + } + + void invoke() + { + m_ensure.step(3); + m_service.invoke(); + } + } +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/DynamicProxyAspectTest.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/DynamicProxyAspectTest.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/DynamicProxyAspectTest.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/DynamicProxyAspectTest.java Sun Jan 31 23:27:05 2016 @@ -0,0 +1,254 @@ +/** + * 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.lambda.itest; + +import static org.apache.felix.dm.lambda.DependencyManagerActivator.aspect; +import static org.apache.felix.dm.lambda.DependencyManagerActivator.component; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import org.apache.felix.dm.Component; +import org.apache.felix.dm.DependencyManager; +import org.junit.Assert; + +/** + * @author <a href="mailto:[email protected]">Felix Project Team</a> + */ +@SuppressWarnings({"rawtypes"}) +public class DynamicProxyAspectTest extends TestBase { + public void testImplementGenericAspectWithDynamicProxyAndFactory() { + DependencyManager m = getDM(); + // helper class that ensures certain steps get executed in sequence + Ensure e = new Ensure(); + + DynamicProxyHandler.resetCounter(); + + // create two service providers, each providing a different service interface + Component sp1 = component(m).impl(new ServiceProvider(e)).provides(ServiceInterface.class).build(); + Component sp2 = component(m).impl(new ServiceProvider2(e)).provides(ServiceInterface2.class).build(); + + // create a dynamic proxy based aspect and hook it up to both services + Component a1 = aspect(m, ServiceInterface.class) + .rank(10) + .autoConfig("m_service") + .factory(new Factory(e, ServiceInterface.class, "ServiceInterfaceProxy"), "create") + .build(); + + Component a2 = aspect(m, ServiceInterface2.class) + .rank(10) + .autoConfig("m_service") + .factory(new Factory(e, ServiceInterface2.class, "ServiceInterfaceProxy2"), "create") + .build(); + + // create a client that invokes a method on boths services, validate that it goes + // through the proxy twice + Component sc = component(m) + .impl(new ServiceConsumer(e)) + .withSrv(ServiceInterface.class, ServiceInterface2.class).build(); + + // register both producers, validate that both services are started + m.add(sp1); + e.waitForStep(1, 2000); + m.add(sp2); + e.waitForStep(2, 2000); + + // add both aspects, and validate that both instances have been created + m.add(a1); + m.add(a2); + e.waitForStep(4, 4000); + + // add the client, which will automatically invoke both services + m.add(sc); + + // wait until both services have been invoked + e.waitForStep(6, 4000); + + // make sure the proxy has been called twice + Assert.assertEquals("Proxy should have been invoked this many times.", 2, DynamicProxyHandler.getCounter()); + + m.remove(sc); + m.remove(a2); + m.remove(a1); + m.remove(sp2); + m.remove(sp1); + m.remove(a2); + m.remove(a1); + } + + public void testImplementGenericAspectWithDynamicProxyAndFactoryRef() { + DependencyManager m = getDM(); + // helper class that ensures certain steps get executed in sequence + Ensure e = new Ensure(); + + DynamicProxyHandler.resetCounter(); + + // create two service providers, each providing a different service interface + Component sp1 = component(m).impl(new ServiceProvider(e)).provides(ServiceInterface.class).build(); + Component sp2 = component(m).impl(new ServiceProvider2(e)).provides(ServiceInterface2.class).build(); + + // create a dynamic proxy based aspect and hook it up to both services + Component a1 = aspect(m, ServiceInterface.class).rank(10).autoConfig("m_service") + .factory(() -> new Factory(e, ServiceInterface.class, "ServiceInterfaceProxy"), Factory::create).build(); + Component a2 = aspect(m, ServiceInterface2.class).rank(10).autoConfig("m_service") + .factory(() -> new Factory(e, ServiceInterface2.class, "ServiceInterfaceProxy2"), Factory::create).build(); + + // create a client that invokes a method on boths services, validate that it goes + // through the proxy twice + Component sc = component(m) + .impl(new ServiceConsumer(e)) + .withSrv(ServiceInterface.class, ServiceInterface2.class).build(); + + // register both producers, validate that both services are started + m.add(sp1); + e.waitForStep(1, 2000); + m.add(sp2); + e.waitForStep(2, 2000); + + // add both aspects, and validate that both instances have been created + m.add(a1); + m.add(a2); + e.waitForStep(4, 4000); + + // add the client, which will automatically invoke both services + m.add(sc); + + // wait until both services have been invoked + e.waitForStep(6, 4000); + + // make sure the proxy has been called twice + Assert.assertEquals("Proxy should have been invoked this many times.", 2, DynamicProxyHandler.getCounter()); + + m.remove(sc); + m.remove(a2); + m.remove(a1); + m.remove(sp2); + m.remove(sp1); + m.remove(a2); + m.remove(a1); + } + + static interface ServiceInterface { + public void invoke(Runnable run); + } + + static interface ServiceInterface2 { + public void invoke(Runnable run); + } + + static class ServiceProvider implements ServiceInterface { + private final Ensure m_ensure; + public ServiceProvider(Ensure e) { + m_ensure = e; + } + public void start() { + m_ensure.step(1); + } + public void invoke(Runnable run) { + run.run(); + } + } + + static class ServiceProvider2 implements ServiceInterface2 { + private final Ensure m_ensure; + public ServiceProvider2(Ensure ensure) { + m_ensure = ensure; + } + public void start() { + m_ensure.step(2); + } + public void invoke(Runnable run) { + run.run(); + } + } + + static class ServiceConsumer implements Runnable { + private volatile ServiceInterface m_service; + private volatile ServiceInterface2 m_service2; + private final Ensure m_ensure; + + public ServiceConsumer(Ensure e) { + m_ensure = e; + } + + public void init() { + Thread t = new Thread(this); + t.start(); + } + + public void run() { + m_service.invoke(Ensure.createRunnableStep(m_ensure, 5)); + m_service2.invoke(Ensure.createRunnableStep(m_ensure, 6)); + } + } + + static class DynamicProxyHandler implements InvocationHandler { + public volatile Object m_service; // ISSUE, we cannot inject into "Object" at the moment + private final String m_label; + private static volatile int m_counter = 0; + + public DynamicProxyHandler(String label) { + m_label = label; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + System.out.println("IIIIIIINVOKE--------------------------" + method.getName()); + if (m_service == null) { + Assert.fail("No service was injected into dynamic proxy handler " + m_label); + } + Method m = m_service.getClass().getMethod(method.getName(), method.getParameterTypes()); + if (m == null) { + Assert.fail("No method " + method.getName() + " was found in instance " + m_service + " in dynamic proxy handler " + m_label); + } + if (method.getName().equals("invoke")) { + // only count methods called 'invoke' because those are actually the ones + // both interfaces implement (and the dynamic proxy might be invoked for + // other methods, such as toString() as well) + m_counter++; + } + return m.invoke(m_service, args); + } + + public static int getCounter() { + return m_counter; + } + + public static void resetCounter() { + m_counter = 0; + } + } + + static class Factory { + private final String m_label; + private Class m_class; + private final Ensure m_ensure; + + public Factory(Ensure ensure, Class clazz, String label) { + m_ensure = ensure; + m_class = clazz; + m_label = label; + } + + public Object create() { + m_ensure.step(); + return Proxy.newProxyInstance(m_class.getClassLoader(), new Class[] { m_class }, new DynamicProxyHandler(m_label)); + } + } +} Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/Ensure.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/Ensure.java?rev=1727869&view=auto ============================================================================== --- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/Ensure.java (added) +++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda.itest/src/org/apache/felix/dm/lambda/itest/Ensure.java Sun Jan 31 23:27:05 2016 @@ -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.lambda.itest; + +import java.io.PrintStream; + +import org.junit.Assert; + +/** + * Helper class to make sure that steps in a test happen in the correct order. Instantiate + * this class and subsequently invoke <code>step(nr)</code> with steps starting at 1. You + * can also have threads wait until you arrive at a certain step. + * + * @author <a href="mailto:[email protected]">Felix Project Team</a> + */ +public class Ensure { + private final boolean DEBUG; + private static long INSTANCE = 0; + private static final int RESOLUTION = 100; + private static PrintStream STREAM = System.out; + int step = 0; + private Throwable m_throwable; + + public Ensure() { + this(true); + } + + public Ensure(boolean debug) { + DEBUG = debug; + if (DEBUG) { + INSTANCE++; + } + } + + public void setStream(PrintStream output) { + STREAM = output; + } + + /** + * Mark this point as step <code>nr</code>. + * + * @param nr the step we are in + */ + public synchronized void step(int nr) { + step++; + Assert.assertEquals(nr, step); + if (DEBUG) { + String info = getLineInfo(3); + STREAM.println("[Ensure " + INSTANCE + "] step " + step + " [" + currentThread() + "] " + info); + } + notifyAll(); + } + + private String getLineInfo(int depth) { + StackTraceElement[] trace = Thread.currentThread().getStackTrace(); + String info = trace[depth].getClassName() + "." + trace[depth].getMethodName() + ":" + trace[depth].getLineNumber(); + return info; + } + + /** + * Mark this point as the next step. + */ + public synchronized void step() { + step++; + if (DEBUG) { + String info = getLineInfo(3); + STREAM.println("[Ensure " + INSTANCE + "] next step " + step + " [" + currentThread() + "] " + info); + } + notifyAll(); + } + + /** + * Wait until we arrive at least at step <code>nr</code> in the process, or fail if that + * takes more than <code>timeout</code> milliseconds. If you invoke wait on a thread, + * you are effectively assuming some other thread will invoke the <code>step(nr)</code> + * method. + * + * @param nr the step to wait for + * @param timeout the number of milliseconds to wait + */ + public synchronized void waitForStep(int nr, int timeout) { + final int initialTimeout = timeout; + if (DEBUG) { + String info = getLineInfo(3); + STREAM.println("[Ensure " + INSTANCE + "] waiting for step " + nr + " [" + currentThread() + "] " + info); + } + while (step < nr && timeout > 0) { + try { + wait(RESOLUTION); + timeout -= RESOLUTION; + } + catch (InterruptedException e) {} + } + if (step < nr) { + throw new IllegalStateException("Timed out waiting for " + initialTimeout + " ms for step " + nr + ", we are still at step " + step); + } + if (DEBUG) { + String info = getLineInfo(3); + STREAM.println("[Ensure " + INSTANCE + "] arrived at step " + nr + " [" + currentThread() + "] " + info); + } + } + + private String currentThread() { + Thread thread = Thread.currentThread(); + return thread.getId() + " " + thread.getName(); + } + + public static Runnable createRunnableStep(final Ensure ensure, final int nr) { + return new Runnable() { public void run() { ensure.step(nr); }}; + } + + public synchronized void steps(Steps steps) { + steps.next(this); + } + + /** + * Helper class for naming a list of step numbers. If used with the steps(Steps) method + * you can define at which steps in time this point should be passed. That means you can + * check methods that will get invoked multiple times during a test. + */ + public static class Steps { + private final int[] m_steps; + private int m_stepIndex; + + /** + * Create a list of steps and initialize the step counter to zero. + */ + public Steps(int... steps) { + m_steps = steps; + m_stepIndex = 0; + } + + /** + * Ensure we're at the right step. Will throw an index out of bounds exception if we enter this step more often than defined. + */ + public void next(Ensure ensure) { + ensure.step(m_steps[m_stepIndex++]); + } + } + + /** + * Saves a thrown exception that occurred in a different thread. You can only save one exception + * at a time this way. + */ + public synchronized void throwable(Throwable throwable) { + m_throwable = throwable; + } + + /** + * Throws a <code>Throwable</code> if one occurred in a different thread and that thread saved it + * using the <code>throwable()</code> method. + */ + public synchronized void ensure() throws Throwable { + if (m_throwable != null) { + throw m_throwable; + } + } +}
