/* * 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 test; import org.apache.felix.dm.Component; import org.apache.felix.dm.ComponentState; import org.apache.felix.dm.ComponentStateListener; import org.apache.felix.dm.Dependency; import org.apache.felix.dm.context.AbstractDependency; import org.apache.felix.dm.impl.ComponentImpl; import org.junit.Assert; import org.junit.Test; /** * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> */ @SuppressWarnings("unused") public class ComponentTest { static class MyComponent { public MyComponent() { } } @Test public void createStartAndStopComponent() { ComponentImpl c = new ComponentImpl(); c.setImplementation(MyComponent.class); Assert.assertEquals("should not be available until started", false, c.isAvailable()); c.start(); Assert.assertEquals("should be available", true, c.isAvailable()); c.stop(); Assert.assertEquals("should no longer be available when stopped", false, c.isAvailable()); } @Test public void testInitCallbackOfComponent() { final Ensure e = new Ensure(); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { void init() { e.step(2); } void start() { e.step(3); } void stop() { e.step(5); } void destroy() { e.step(6); } }); e.step(1); c.start(); e.step(4); c.stop(); e.step(7); } @Test public void testAddDependencyFromInitCallback() { final Ensure e = new Ensure(); final SimpleServiceDependency d = new SimpleServiceDependency(); d.setRequired(true); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { void init(Component c) { e.step(2); c.add(d); } void start() { e.step(4); } void stop() { e.step(6); } void destroy() { e.step(7); } }); e.step(1); c.start(); e.step(3); d.add(new EventImpl()); // NPE?! e.step(5); d.remove(new EventImpl()); c.stop(); e.step(8); } @Test public void testAddAvailableDependencyFromInitCallback() { final Ensure e = new Ensure(); final SimpleServiceDependency d = new SimpleServiceDependency(); d.setRequired(true); final SimpleServiceDependency d2 = new SimpleServiceDependency(); d2.setRequired(true); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { void init(Component c) { System.out.println("init"); e.step(2); c.add(d); d.add(new EventImpl()); c.add(d2); } void start() { System.out.println("start"); e.step(); } void stop() { System.out.println("stop"); e.step(); } void destroy() { System.out.println("destroy"); e.step(7); } }); e.step(1); c.start(); e.step(3); d2.add(new EventImpl()); e.step(5); d.remove(new EventImpl()); c.stop(); e.step(8); } @Test public void testAtomicallyAddMultipleDependenciesFromInitCallback() { final Ensure e = new Ensure(); final SimpleServiceDependency d = new SimpleServiceDependency(); d.setRequired(true); final SimpleServiceDependency d2 = new SimpleServiceDependency(); d2.setRequired(true); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { void init(Component c) { System.out.println("init"); e.step(2); c.add(d, d2); d.add(new EventImpl()); // won't trigger start because d2 is not yet available } void start() { System.out.println("start"); e.step(4); } void stop() { System.out.println("stop"); e.step(6); } void destroy() { System.out.println("destroy"); e.step(7); } }); e.step(1); c.start(); e.step(3); d2.add(new EventImpl()); e.step(5); d.remove(new EventImpl()); c.stop(); e.step(8); } @Test public void createComponentAddDependencyAndStartComponent() { ComponentImpl c = new ComponentImpl(); c.setImplementation(MyComponent.class); SimpleServiceDependency d = new SimpleServiceDependency(); d.setRequired(true); c.add(d); c.start(); Assert.assertEquals("should not be available when started because of missing dependency", false, c.isAvailable()); c.stop(); c.remove(d); } @Test public void createComponentStartItAndAddDependency() { ComponentImpl c = new ComponentImpl(); c.setImplementation(MyComponent.class); SimpleServiceDependency d = new SimpleServiceDependency(); d.setRequired(true); c.start(); Assert.assertEquals("should be available when started", true, c.isAvailable()); c.add(d); Assert.assertEquals("dependency should not be available", false, d.isAvailable()); Assert.assertEquals("Component should not be available", false, c.isAvailable()); c.remove(d); c.stop(); } @Test public void createComponentStartItAddDependencyAndMakeDependencyAvailable() { ComponentImpl c = new ComponentImpl(); c.setImplementation(MyComponent.class); SimpleServiceDependency d = new SimpleServiceDependency(); d.setRequired(true); c.start(); c.add(d); Assert.assertEquals("Component should not be available: it is started but the dependency is not available", false, c.isAvailable()); d.add(new EventImpl()); Assert.assertEquals("dependency is available, component should be too", true, c.isAvailable()); d.remove(new EventImpl()); Assert.assertEquals("dependency is no longer available, component should not be either", false, c.isAvailable()); c.remove(d); Assert.assertEquals("dependency is removed, component should be available again", true, c.isAvailable()); c.stop(); Assert.assertEquals("Component is stopped, should be unavailable now", false, c.isAvailable()); } @Test public void createComponentStartItAddDependencyAndListenerMakeDependencyAvailableAndUnavailableImmediately() { ComponentImpl c = new ComponentImpl(); c.setImplementation(MyComponent.class); final SimpleServiceDependency d = new SimpleServiceDependency(); d.setRequired(true); ComponentStateListener l = new ComponentStateListener() { @Override public void changed(Component c, ComponentState state) { // make the dependency unavailable d.remove(new EventImpl()); } }; c.start(); c.add(d); // we add a listener here which immediately triggers an 'external event' that // makes the dependency unavailable again as soon as it's invoked c.add(l); Assert.assertEquals("Component unavailable, dependency unavailable", false, c.isAvailable()); // so even though we make the dependency available here, before our call returns it // is made unavailable again d.add(new EventImpl()); Assert.assertEquals("Component *still* unavailable, because the listener immediately makes the dependency unavailable", false, c.isAvailable()); c.remove(l); Assert.assertEquals("listener removed, component still unavailable", false, c.isAvailable()); c.remove(d); Assert.assertEquals("dependency removed, component available", true, c.isAvailable()); c.stop(); Assert.assertEquals("Component stopped, should be unavailable", false, c.isAvailable()); } @Test public void createComponentAddTwoDependenciesMakeBothAvailableAndUnavailable() { ComponentImpl c = new ComponentImpl(); c.setImplementation(MyComponent.class); SimpleServiceDependency d1 = new SimpleServiceDependency(); d1.setRequired(true); SimpleServiceDependency d2 = new SimpleServiceDependency(); d2.setRequired(true); c.start(); c.add(d1); c.add(d2); Assert.assertEquals("Component should be unavailable, both dependencies are too", false, c.isAvailable()); d1.add(new EventImpl()); Assert.assertEquals("one dependency available, component should still be unavailable", false, c.isAvailable()); d2.add(new EventImpl()); Assert.assertEquals("both dependencies available, component should be available", true, c.isAvailable()); d1.remove(new EventImpl()); Assert.assertEquals("one dependency unavailable again, component should be unavailable too", false, c.isAvailable()); d2.remove(new EventImpl()); Assert.assertEquals("both dependencies unavailable, component should be too", false, c.isAvailable()); c.remove(d2); Assert.assertEquals("removed one dependency, still unavailable", false, c.isAvailable()); c.remove(d1); Assert.assertEquals("removed the other dependency, component should be available now", true, c.isAvailable()); c.stop(); Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable()); } @Test public void createComponentAddDependencyMakeAvailableAndUnavailableWithCallbacks() { final Ensure e = new Ensure(); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { public void add() { e.step(1); } public void remove() { e.step(3); } }); SimpleServiceDependency d1 = new SimpleServiceDependency(); d1.setCallbacks("add", "remove"); d1.setRequired(true); // add the dependency to the component c.add(d1); // start the component c.start(); // make the dependency available, we expect the add callback // to be invoked here d1.add(new EventImpl()); e.step(2); // remove the dependency, should trigger the remove callback d1.remove(new EventImpl()); e.step(4); c.stop(); c.remove(d1); Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable()); } @Test public void createAndStartComponentAddDependencyMakeAvailableAndUnavailableWithCallbacks() { final Ensure e = new Ensure(); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { public void add() { e.step(1); } public void remove() { e.step(3); } }); SimpleServiceDependency d1 = new SimpleServiceDependency(); d1.setCallbacks("add", "remove"); d1.setRequired(true); // start the ComponentImpl (it should become available) c.start(); // add the dependency (it should become unavailable) c.add(d1); // make the dependency available, which should invoke the // add callback d1.add(new EventImpl()); e.step(2); // make the dependency unavailable, should trigger the // remove callback d1.remove(new EventImpl()); e.step(4); c.remove(d1); c.stop(); Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable()); } @Test public void createComponentAddTwoDependenciesMakeBothAvailableAndUnavailableWithCallbacks() { final Ensure e = new Ensure(); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { public void add() { e.step(); } public void remove() { e.step(); } }); SimpleServiceDependency d1 = new SimpleServiceDependency(); d1.setCallbacks("add", "remove"); d1.setRequired(true); SimpleServiceDependency d2 = new SimpleServiceDependency(); d2.setCallbacks("add", "remove"); d2.setRequired(true); // start the component, which should become active because there are no // dependencies yet c.start(); // now add the dependencies, making the ComponentImpl unavailable c.add(d1); c.add(d2); // make the first dependency available, should have no effect on the // component d1.add(new EventImpl()); e.step(1); // second dependency available, now all the add callbacks should be // invoked d2.add(new EventImpl()); e.step(4); // remove the first dependency, triggering the remove callbacks d1.remove(new EventImpl()); e.step(7); // remove the second dependency, should not trigger more callbacks d2.remove(new EventImpl()); e.step(8); c.remove(d2); c.remove(d1); c.stop(); // still, no more callbacks should have been invoked e.step(9); Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable()); } @Test public void createAndStartComponentAddTwoDependenciesMakeBothAvailableAndUnavailableWithCallbacks() { final Ensure e = new Ensure(); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { public void add() { e.step(); } public void remove() { e.step(); } }); // start the component, it should become available c.start(); SimpleServiceDependency d1 = new SimpleServiceDependency(); d1.setCallbacks("add", "remove"); d1.setRequired(true); SimpleServiceDependency d2 = new SimpleServiceDependency(); d2.setCallbacks("add", "remove"); d2.setRequired(true); // add the first dependency, ComponentImpl should be unavailable c.add(d1); c.add(d2); // make first dependency available, ComponentImpl should still be unavailable d1.add(new EventImpl()); e.step(1); // make second dependency available, ComponentImpl available, callbacks should // be invoked d2.add(new EventImpl()); e.step(4); // remove the first dependency, callbacks should be invoked d1.remove(new EventImpl()); e.step(7); // remove second dependency, no callbacks should be invoked d2.remove(new EventImpl()); e.step(8); c.remove(d2); c.remove(d1); c.stop(); e.step(9); Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable()); } @Test public void createAndStartComponentAddTwoDependenciesWithMultipleServicesWithCallbacks() { final Ensure e = new Ensure(); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { public void add() { e.step(); } public void remove() { e.step(); } }); // start component c.start(); SimpleServiceDependency d1 = new SimpleServiceDependency(); d1.setCallbacks("add", "remove"); d1.setRequired(true); SimpleServiceDependency d2 = new SimpleServiceDependency(); d2.setCallbacks("add", "remove"); d2.setRequired(true); c.add(d1); c.add(d2); // add three instances to first dependency, no callbacks should // be triggered d1.add(new EventImpl(1)); d1.add(new EventImpl(2)); d1.add(new EventImpl(3)); e.step(1); // add two instances to the second dependency, callbacks should // be invoked (4x) d2.add(new EventImpl(1)); e.step(6); // add another dependency, triggering another callback d2.add(new EventImpl(2)); e.step(8); // remove first dependency (all three of them) which makes the // ComponentImpl unavailable so it should trigger calling remove for // all of them (so 5x) d1.remove(new EventImpl(1)); d1.remove(new EventImpl(2)); d1.remove(new EventImpl(3)); e.step(14); // remove second dependency, should not trigger further callbacks d2.remove(new EventImpl(1)); d2.remove(new EventImpl(2)); e.step(15); c.remove(d2); c.remove(d1); c.stop(); e.step(16); Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable()); } @Test public void createComponentAddDependencyMakeAvailableChangeAndUnavailableWithCallbacks() { final Ensure e = new Ensure(); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { public void add() { e.step(1); } public void change() { e.step(3); } public void remove() { e.step(5); } }); SimpleServiceDependency d1 = new SimpleServiceDependency(); d1.setCallbacks("add", "change", "remove"); d1.setRequired(true); // add the dependency to the component c.add(d1); // start the component c.start(); // make the dependency available, we expect the add callback // to be invoked here d1.add(new EventImpl()); e.step(2); // change the dependency d1.change(new EventImpl()); e.step(4); // remove the dependency, should trigger the remove callback d1.remove(new EventImpl()); e.step(6); c.stop(); c.remove(d1); Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable()); } @Test public void createComponentWithOptionalDependency() { final Ensure e = new Ensure(); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { public void add() { e.step(1); } public void change() { e.step(3); } public void remove() { e.step(5); } }); SimpleServiceDependency d1 = new SimpleServiceDependency(); d1.setCallbacks("add", "change", "remove"); d1.setRequired(false); // add the dependency to the component c.add(d1); // start the component c.start(); Assert.assertEquals("Component started with an optional dependency, should be available", true, c.isAvailable()); // make the dependency available, we expect the add callback // to be invoked here d1.add(new EventImpl()); e.step(2); Assert.assertEquals("Component started with an optional dependency, should be available", true, c.isAvailable()); // change the dependency d1.change(new EventImpl()); e.step(4); Assert.assertEquals("Component started with an optional dependency, should be available", true, c.isAvailable()); // remove the dependency, should trigger the remove callback d1.remove(new EventImpl()); Assert.assertEquals("Component started with an optional dependency, should be available", true, c.isAvailable()); e.step(6); c.stop(); c.remove(d1); Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable()); } @Test public void createComponentWithOptionalAndRequiredDependency() { final Ensure e = new Ensure(); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { public void add() { e.step(); } public void remove() { e.step(); } }); SimpleServiceDependency d1 = new SimpleServiceDependency(); d1.setCallbacks("add", "remove"); d1.setRequired(false); SimpleServiceDependency d2 = new SimpleServiceDependency(); d2.setCallbacks("add", "remove"); d2.setRequired(true); // add the dependencies to the component c.add(d1); c.add(d2); // start the component c.start(); Assert.assertEquals("Component started with a required and optional dependency, should not be available", false, c.isAvailable()); // make the optional dependency available d1.add(new EventImpl()); e.step(1); Assert.assertEquals("Component should not be available", false, c.isAvailable()); // make the required dependency available d2.add(new EventImpl()); e.step(4); Assert.assertEquals("Component should be available", true, c.isAvailable()); // remove the optional dependency d1.remove(new EventImpl()); e.step(6); Assert.assertEquals("Component should be available", true, c.isAvailable()); // remove the required dependency d1.remove(new EventImpl()); e.step(8); Assert.assertEquals("Component should be available", true, c.isAvailable()); c.stop(); c.remove(d1); Assert.assertEquals("Component stopped, should be unavailable again", false, c.isAvailable()); } @Test public void createComponentAddAvailableDependencyRemoveDependencyCheckStopCalledBeforeUnbind() { final Ensure e = new Ensure(); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { void add() { e.step(1); } void start() { e.step(2); } void stop() { e.step(4); } void remove() { e.step(5); } }); SimpleServiceDependency d = new SimpleServiceDependency(); d.setCallbacks("add", "remove"); d.setRequired(true); // add the dependency to the component c.add(d); // start the component c.start(); // make the dependency available, we expect the add callback // to be invoked here, then start is called. d.add(new EventImpl()); e.step(3); // remove the dependency, should trigger the stop, then remove callback d.remove(new EventImpl()); e.step(6); c.stop(); c.remove(d); Assert.assertEquals("Component stopped, should be unavailable", false, c.isAvailable()); } @Test public void createDependenciesWithCallbackInstance() { final Ensure e = new Ensure(); ComponentImpl c = new ComponentImpl(); c.setImplementation(new Object() { void start() { e.step(2); } void stop() { e.step(4); } }); Object callbackInstance = new Object() { void add() { e.step(1); } void remove() { e.step(5); } }; SimpleServiceDependency d = new SimpleServiceDependency(); d.setCallbacks(callbackInstance, "add", "remove"); d.setRequired(true); // add the dependency to the component c.add(d); // start the component c.start(); // make the dependency available, we expect the add callback // to be invoked here, then start is called. d.add(new EventImpl()); e.step(3); // remove the dependency, should trigger the stop, then remove callback d.remove(new EventImpl()); e.step(6); c.stop(); c.remove(d); Assert.assertEquals("Component stopped, should be unavailable", false, c.isAvailable()); } }