/* * 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.ipojo.runtime.core; import org.apache.felix.ipojo.ComponentInstance; import org.apache.felix.ipojo.handlers.providedservice.ProvidedService; import org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceDescription; import org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceHandlerDescription; import org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceListener; import org.apache.felix.ipojo.runtime.core.services.CheckService; import org.apache.felix.ipojo.runtime.core.services.FooService; import org.junit.Test; import java.util.*; import static org.junit.Assert.*; /** * Test that the ProvidedServiceHandler calls the ProvidedServiceHandlerListeners when watched provided service changes. */ public class TestListeners extends Common { /** * The number of provided service registrations, incremented by the {@code CountingListener}s. */ int registrations = 0; /** * The number of provided service unregistrations, incremented by the {@code CountingListener}s. */ int unregistrations = 0; /** * The number of provided service updates, incremented by the {@code CountingListener}s. */ int updates = 0; /** * The total number of provided service events, incremented by the {@code TotalCountingListener}s. */ int total = 0; /** * The {@code instance} arguments received by the {@code AppendingListener}s. */ List<ComponentInstance> instances = new ArrayList<ComponentInstance>(); /** * A ProvidedServiceListener that just count service registrations, updates and unregistrations. */ private class CountingListener implements ProvidedServiceListener { public void serviceRegistered(ComponentInstance instance, ProvidedService providedService) { registrations++; } public void serviceModified(ComponentInstance instance, ProvidedService providedService) { updates++; } public void serviceUnregistered(ComponentInstance instance, ProvidedService providedService) { unregistrations++; } } /** * A ProvidedServiceListener that just count events. */ private class TotalCountingListener implements ProvidedServiceListener { public void serviceRegistered(ComponentInstance instance, ProvidedService providedService) { total++; } public void serviceModified(ComponentInstance instance, ProvidedService providedService) { total++; } public void serviceUnregistered(ComponentInstance instance, ProvidedService providedService) { total++; } } /** * A ProvidedServiceListener that just fails. * <p> * Used to ensure that a failing listener does not prevent the following listeners to be called. * </p> */ private class ThrowingListener implements ProvidedServiceListener { public void serviceRegistered(ComponentInstance instance, ProvidedService providedService) { throw new RuntimeException("I'm bad"); } public void serviceModified(ComponentInstance instance, ProvidedService providedService) { throw new RuntimeException("I'm bad"); } public void serviceUnregistered(ComponentInstance instance, ProvidedService providedService) { throw new RuntimeException("I'm bad"); } } /** * A ProvidedServiceListener that just appends its arguments. */ private class AppendingListener implements ProvidedServiceListener { public void serviceRegistered(ComponentInstance instance, ProvidedService providedService) { instances.add(instance); } public void serviceModified(ComponentInstance instance, ProvidedService providedService) { instances.add(instance); } public void serviceUnregistered(ComponentInstance instance, ProvidedService providedService) { instances.add(instance); } } /** * Test that the listeners registered on the tested instance are called when the instance provided service is * registered, updated or unregistered. */ @Test public void testProvidedServiceListener() { ComponentInstance ci = ipojoHelper.createComponentInstance("PS-Controller-1-default"); ProvidedServiceHandlerDescription pshd = (ProvidedServiceHandlerDescription) ci.getInstanceDescription() .getHandlerDescription("org.apache.felix.ipojo:provides"); ProvidedServiceDescription ps = getPS(FooService.class.getName(), pshd.getProvidedServices()); // Controller set to true. osgiHelper.waitForService(FooService.class.getName(), null, 5000); osgiHelper.waitForService(CheckService.class.getName(), null, 5000); CheckService check = (CheckService) osgiHelper.getServiceObject(CheckService.class.getName(), null); assertNotNull(check); // Register listeners : // 1- CountingListener l1 // 2- ThrowingListener bad // 3- TotalCountingListener l2 // 4- AppendingListener l3 ProvidedServiceListener l1 = new CountingListener(); ps.addListener(l1); ProvidedServiceListener bad = new ThrowingListener(); ps.addListener(bad); ProvidedServiceListener l2 = new TotalCountingListener(); ps.addListener(l2); ProvidedServiceListener l3 = new AppendingListener(); ps.addListener(l3); // Check initial valued are untouched assertEquals(0, registrations); assertEquals(0, unregistrations); assertEquals(0, updates); assertEquals(0, total); // Unregister the service and check. assertFalse(check.check()); assertEquals(0, registrations); assertEquals(1, unregistrations); assertEquals(0, updates); assertEquals(1, total); // Modify the service while it is unregistered. Nothing should move. Hashtable<String, Object> props = new Hashtable<String, Object>(); props.put("change1", "1"); ps.addProperties(props); assertEquals(0, registrations); assertEquals(1, unregistrations); assertEquals(0, updates); assertEquals(1, total); // Register the service and check. assertTrue(check.check()); assertEquals(1, registrations); assertEquals(1, unregistrations); assertEquals(0, updates); assertEquals(2, total); // Modify the service while it is REGISTERED props.clear(); props.put("change2", "2"); ps.addProperties(props); assertEquals(1, registrations); assertEquals(1, unregistrations); assertEquals(1, updates); assertEquals(3, total); // One more time, just to be sure... assertFalse(check.check()); // Unregister assertEquals(1, registrations); assertEquals(2, unregistrations); assertEquals(1, updates); assertEquals(4, total); assertTrue(check.check()); // Register assertEquals(2, registrations); assertEquals(2, unregistrations); assertEquals(1, updates); assertEquals(5, total); // Unregister the listener ps.removeListener(l1); ps.removeListener(bad); ps.removeListener(l2); ps.removeListener(l3); // Play with the controller and check that nothing moves assertFalse(check.check()); // Unregister assertEquals(2, registrations); assertEquals(2, unregistrations); assertEquals(1, updates); assertEquals(5, total); assertTrue(check.check()); // Register assertEquals(2, registrations); assertEquals(2, unregistrations); assertEquals(1, updates); assertEquals(5, total); props.clear(); props.put("change3", "3"); ps.addProperties(props); // Modify assertEquals(2, registrations); assertEquals(2, unregistrations); assertEquals(1, updates); assertEquals(5, total); // Check that instances contains $total times the ci component instance, and nothing else. assertEquals(5, instances.size()); instances.removeAll(Collections.singleton(ci)); assertEquals(0, instances.size()); ci.dispose(); } @Test(expected = NullPointerException.class) public void testNullProvidedServiceListener() { ComponentInstance ci = ipojoHelper.createComponentInstance("PS-Controller-1-default"); ProvidedServiceHandlerDescription pshd = (ProvidedServiceHandlerDescription) ci.getInstanceDescription() .getHandlerDescription("org.apache.felix.ipojo:provides"); ProvidedServiceDescription ps = getPS(FooService.class.getName(), pshd.getProvidedServices()); // Should fail! ps.addListener(null); } @Test(expected = NoSuchElementException.class) public void testRemoveNonexistentProvidedServiceListener() { ComponentInstance ci = ipojoHelper.createComponentInstance("PS-Controller-1-default"); ProvidedServiceHandlerDescription pshd = (ProvidedServiceHandlerDescription) ci.getInstanceDescription() .getHandlerDescription("org.apache.felix.ipojo:provides"); ProvidedServiceDescription ps = getPS(FooService.class.getName(), pshd.getProvidedServices()); // Should fail! ps.removeListener(new ThrowingListener()); } private ProvidedServiceDescription getPS(String itf, ProvidedServiceDescription[] svc) { for (int i = 0; i < svc.length; i++) { if (svc[i].getServiceSpecifications()[0].equals(itf)) { return svc[i]; } } fail("Service : " + itf + " not found"); return null; } }