Fix bug in whiteboard service update/remove
The removal code did not correctly clear instances from the type specific id maps, or from the wildcard maps. This commit adds a test for the failing case and also fixes the map management logic.
Signed-off-by: Tim Ward <timothyjward@apache.org>
diff --git a/org.apache.aries.typedevent.bus/src/main/java/org/apache/aries/typedevent/bus/impl/TypedEventBusImpl.java b/org.apache.aries.typedevent.bus/src/main/java/org/apache/aries/typedevent/bus/impl/TypedEventBusImpl.java
index e055443..9c4057e 100644
--- a/org.apache.aries.typedevent.bus/src/main/java/org/apache/aries/typedevent/bus/impl/TypedEventBusImpl.java
+++ b/org.apache.aries.typedevent.bus/src/main/java/org/apache/aries/typedevent/bus/impl/TypedEventBusImpl.java
@@ -271,7 +271,7 @@
Long serviceId = getServiceId(properties);
- doRemoveEventHandler(topicsToTypedHandlers, knownTypedHandlers, handler, serviceId);
+ doRemoveEventHandler(topicsToTypedHandlers, wildcardTopicsToTypedHandlers, knownTypedHandlers, handler, serviceId);
synchronized (lock) {
typedHandlersToTargetClasses.remove(handler);
@@ -282,7 +282,7 @@
Long serviceId = getServiceId(properties);
- doRemoveEventHandler(topicsToUntypedHandlers, knownUntypedHandlers, handler, serviceId);
+ doRemoveEventHandler(topicsToUntypedHandlers, wildcardTopicsToUntypedHandlers, knownUntypedHandlers, handler, serviceId);
}
private Long getServiceId(Map<String, Object> properties) {
@@ -307,14 +307,20 @@
}
}
- private <T, U> void doRemoveEventHandler(Map<String, Map<T, U>> map, Map<Long, T> idMap,
+ private <T, U> void doRemoveEventHandler(Map<String, Map<T, U>> map, Map<String, Map<T, U>> wildcardMap, Map<Long, T> idMap,
T handler, Long serviceId) {
synchronized (lock) {
List<String> consumed = knownHandlers.remove(serviceId);
- knownHandlers.remove(serviceId);
+ idMap.remove(serviceId);
if (consumed != null) {
consumed.forEach(s -> {
- Map<T, ?> handlers = map.get(s);
+ Map<T, ?> handlers;
+ if(isWildcard(s)) {
+ handlers = wildcardMap.get(s.length() == 1 ? "" : s.substring(0, s.length() - 2));
+ } else {
+ handlers = map.get(s);
+ }
+
if (handlers != null) {
handlers.remove(handler);
if (handlers.isEmpty()) {
@@ -350,7 +356,7 @@
synchronized (lock) {
T handler = idToHandler.get(serviceId);
- doRemoveEventHandler(map, idToHandler, handler, serviceId);
+ doRemoveEventHandler(map, wildcardMap, idToHandler, handler, serviceId);
doAddEventHandler(map, wildcardMap, idToHandler, handler, defaultTopic, properties);
}
}
diff --git a/org.apache.aries.typedevent.bus/src/test/java/org/apache/aries/typedevent/bus/osgi/EventDeliveryIntegrationTest.java b/org.apache.aries.typedevent.bus/src/test/java/org/apache/aries/typedevent/bus/osgi/EventDeliveryIntegrationTest.java
index 40543fa..96d0b65 100644
--- a/org.apache.aries.typedevent.bus/src/test/java/org/apache/aries/typedevent/bus/osgi/EventDeliveryIntegrationTest.java
+++ b/org.apache.aries.typedevent.bus/src/test/java/org/apache/aries/typedevent/bus/osgi/EventDeliveryIntegrationTest.java
@@ -18,6 +18,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
+import static org.osgi.service.typedevent.TypedEventConstants.TYPED_EVENT_TOPICS;
import java.util.Dictionary;
import java.util.HashMap;
@@ -224,6 +225,104 @@
assertEquals("foo", received.subEvent.message);
}
-
+ /**
+ * Tests that events are delivered to untyped Event Handlers
+ * based on topic
+ *
+ * @throws InterruptedException
+ */
+ @Test
+ public void testEventReceivingUpdateTopic() throws InterruptedException {
+
+ TestEvent event = new TestEvent();
+ event.message = "boo";
+
+ Dictionary<String, Object> props = new Hashtable<>();
+ props.put(TYPED_EVENT_TOPICS, TEST_EVENT_TOPIC);
+
+ regs.add(context.registerService(TypedEventHandler.class, typedEventHandler, props));
+
+ regs.add(context.registerService(UntypedEventHandler.class, untypedEventHandler, props));
+
+ eventBus.deliver(event);
+
+ Mockito.verify(typedEventHandler, Mockito.timeout(1000)).notify(
+ Mockito.eq(TEST_EVENT_TOPIC), Mockito.argThat(isTestEventWithMessage("boo")));
+
+ Mockito.verify(untypedEventHandler, Mockito.timeout(1000)).notifyUntyped(
+ Mockito.eq(TEST_EVENT_TOPIC), Mockito.argThat(isUntypedTestEventWithMessage("boo")));
+
+ Mockito.clearInvocations(typedEventHandler, untypedEventHandler);
+
+ props.put(TYPED_EVENT_TOPICS, TEST_EVENT_2_TOPIC);
+
+ regs.forEach(s -> s.setProperties(props));
+
+ eventBus.deliver(event);
+
+ Mockito.verify(typedEventHandler, Mockito.after(1000).never()).notify(
+ Mockito.eq(TEST_EVENT_TOPIC), Mockito.any());
+ Mockito.verify(untypedEventHandler, Mockito.after(1000).never()).notifyUntyped(
+ Mockito.eq(TEST_EVENT_TOPIC), Mockito.any());
+
+ eventBus.deliver(TEST_EVENT_2_TOPIC, event);
+
+ Mockito.verify(typedEventHandler, Mockito.timeout(1000)).notify(
+ Mockito.eq(TEST_EVENT_2_TOPIC), Mockito.argThat(isTestEventWithMessage("boo")));
+
+ Mockito.verify(untypedEventHandler, Mockito.timeout(1000)).notifyUntyped(
+ Mockito.eq(TEST_EVENT_2_TOPIC), Mockito.argThat(isUntypedTestEventWithMessage("boo")));
+
+ }
+
+ /**
+ * Tests that events are delivered to untyped Event Handlers
+ * based on topic
+ *
+ * @throws InterruptedException
+ */
+ @Test
+ public void testEventReceivingUpdateWildcardTopic() throws InterruptedException {
+
+ TestEvent event = new TestEvent();
+ event.message = "boo";
+
+ Dictionary<String, Object> props = new Hashtable<>();
+ props.put(TYPED_EVENT_TOPICS, "foo/bar/*");
+
+ regs.add(context.registerService(TypedEventHandler.class, typedEventHandler, props));
+
+ regs.add(context.registerService(UntypedEventHandler.class, untypedEventHandler, props));
+
+ eventBus.deliver("foo/bar/foobar", event);
+
+ Mockito.verify(typedEventHandler, Mockito.timeout(1000)).notify(
+ Mockito.eq("foo/bar/foobar"), Mockito.argThat(isTestEventWithMessage("boo")));
+
+ Mockito.verify(untypedEventHandler, Mockito.timeout(1000)).notifyUntyped(
+ Mockito.eq("foo/bar/foobar"), Mockito.argThat(isUntypedTestEventWithMessage("boo")));
+
+ Mockito.clearInvocations(typedEventHandler, untypedEventHandler);
+
+ props.put(TYPED_EVENT_TOPICS, "foo/bar/foobar/*");
+
+ regs.forEach(s -> s.setProperties(props));
+
+ eventBus.deliver("foo/bar/foobar", event);
+
+ Mockito.verify(typedEventHandler, Mockito.after(1000).never()).notify(
+ Mockito.eq("foo/bar/foobar"), Mockito.any());
+ Mockito.verify(untypedEventHandler, Mockito.after(1000).never()).notifyUntyped(
+ Mockito.eq("foo/bar/foobar"), Mockito.any());
+
+ eventBus.deliver("foo/bar/foobar/fizzbuzz", event);
+
+ Mockito.verify(typedEventHandler, Mockito.timeout(1000)).notify(
+ Mockito.eq("foo/bar/foobar/fizzbuzz"), Mockito.argThat(isTestEventWithMessage("boo")));
+
+ Mockito.verify(untypedEventHandler, Mockito.timeout(1000)).notifyUntyped(
+ Mockito.eq("foo/bar/foobar/fizzbuzz"), Mockito.argThat(isUntypedTestEventWithMessage("boo")));
+
+ }
}
\ No newline at end of file