diff --git a/iddd_common/src/main/java/com/saasovation/common/domain/model/DomainEventPublisher.java b/iddd_common/src/main/java/com/saasovation/common/domain/model/DomainEventPublisher.java index f14aa7aa..8c623f50 100644 --- a/iddd_common/src/main/java/com/saasovation/common/domain/model/DomainEventPublisher.java +++ b/iddd_common/src/main/java/com/saasovation/common/domain/model/DomainEventPublisher.java @@ -36,7 +36,7 @@ public static DomainEventPublisher instance() { } public void publish(final T aDomainEvent) { - if (!this.isPublishing() && this.hasSubscribers()) { + if (this.hasSubscribers()) { try { this.setPublishing(true); @@ -49,7 +49,7 @@ public void publish(final T aDomainEvent) { for (DomainEventSubscriber subscriber : allSubscribers) { Class subscribedToType = subscriber.subscribedToEventType(); - if (eventType == subscribedToType || subscribedToType == DomainEvent.class) { + if (eventType == subscribedToType || subscribedToType == DomainEvent.class || subscribedToType.isAssignableFrom(eventType)) { subscriber.handleEvent(aDomainEvent); } } @@ -67,18 +67,18 @@ public void publishAll(Collection aDomainEvents) { } public void reset() { - if (!this.isPublishing()) { - this.setSubscribers(null); - } + this.setSubscribers(null); } @SuppressWarnings("unchecked") public void subscribe(DomainEventSubscriber aSubscriber) { - if (!this.isPublishing()) { - this.ensureSubscribersList(); - - this.subscribers().add(aSubscriber); + if (this.isPublishing()) { + throw new IllegalStateException("cannot subscribe while handling an event"); } + + this.ensureSubscribersList(); + + this.subscribers().add(aSubscriber); } private DomainEventPublisher() { diff --git a/iddd_common/src/test/java/com/saasovation/common/event/AKindOfTestableDomainEvent.java b/iddd_common/src/test/java/com/saasovation/common/event/AKindOfTestableDomainEvent.java new file mode 100644 index 00000000..21b8be8f --- /dev/null +++ b/iddd_common/src/test/java/com/saasovation/common/event/AKindOfTestableDomainEvent.java @@ -0,0 +1,15 @@ +package com.saasovation.common.event; + +public class AKindOfTestableDomainEvent extends TestableDomainEvent { + private Integer value; + + public AKindOfTestableDomainEvent(long anId, String aName, Integer aValue) { + super(anId, aName); + } + + public Integer value() { return value; } + + private void setValue(Integer value) { + this.value = value; + } +} diff --git a/iddd_common/src/test/java/com/saasovation/common/event/DomainEventPublisherTest.java b/iddd_common/src/test/java/com/saasovation/common/event/DomainEventPublisherTest.java index 80528807..161529bb 100644 --- a/iddd_common/src/test/java/com/saasovation/common/event/DomainEventPublisherTest.java +++ b/iddd_common/src/test/java/com/saasovation/common/event/DomainEventPublisherTest.java @@ -28,9 +28,12 @@ public DomainEventPublisherTest() { super(); } - public void testDomainEventPublisherPublish() throws Exception { + @Override + protected void setUp() throws Exception { DomainEventPublisher.instance().reset(); + } + public void testDomainEventPublisherPublish() throws Exception { DomainEventPublisher.instance().subscribe(new DomainEventSubscriber(){ @Override public void handleEvent(TestableDomainEvent aDomainEvent) { @@ -51,18 +54,15 @@ public Class subscribedToEventType() { assertTrue(this.eventHandled); } - public void testDomainEventPublisherBlocked() throws Exception { - DomainEventPublisher.instance().reset(); - DomainEventPublisher.instance().subscribe(new DomainEventSubscriber(){ + public void testCanPublishInEventHandler() throws Exception { + DomainEventPublisher.instance().subscribe(new DomainEventSubscriber() { @Override public void handleEvent(TestableDomainEvent aDomainEvent) { - assertEquals(100L, aDomainEvent.id()); - assertEquals("test", aDomainEvent.name()); eventHandled = true; - // attempt nested publish, which should not work DomainEventPublisher.instance().publish(new AnotherTestableDomainEvent(1000.0)); } + @Override public Class subscribedToEventType() { return TestableDomainEvent.class; @@ -72,7 +72,6 @@ public Class subscribedToEventType() { DomainEventPublisher.instance().subscribe(new DomainEventSubscriber(){ @Override public void handleEvent(AnotherTestableDomainEvent aDomainEvent) { - // should never be reached due to blocked publisher assertEquals(1000.0, aDomainEvent.value()); anotherEventHandled = true; } @@ -88,6 +87,66 @@ public Class subscribedToEventType() { DomainEventPublisher.instance().publish(new TestableDomainEvent(100L, "test")); assertTrue(this.eventHandled); + assertTrue(this.anotherEventHandled); + } + + public void testCannotSubscribeWhenPublishing() throws Exception { + final DomainEventSubscriber anotherHandler = new DomainEventSubscriber() { + @Override + public void handleEvent(AnotherTestableDomainEvent aDomainEvent) { + // should never be reached due to incapacitated handler + anotherEventHandled = true; + } + + @Override + public Class subscribedToEventType() { + return AnotherTestableDomainEvent.class; + } + }; + + DomainEventPublisher.instance().subscribe(new DomainEventSubscriber(){ + @Override + public void handleEvent(TestableDomainEvent aDomainEvent) { + eventHandled = true; + DomainEventPublisher.instance().subscribe(anotherHandler); + } + @Override + public Class subscribedToEventType() { + return TestableDomainEvent.class; + } + }); + + try { + DomainEventPublisher.instance().publish(new TestableDomainEvent(100L, "test")); + fail("the event subscriber should not be allowed to complete as it's trying to modify the publisher's subscribers while handling an event (causing a ConcurrentModificationException)"); + } catch(IllegalStateException e) { + // we're good + } + assertFalse(this.anotherEventHandled); + + DomainEventPublisher.instance().publish(new AnotherTestableDomainEvent(1000.0)); + + assertFalse(this.anotherEventHandled); + } + + public void testPublishesEventsInheritingFromTheSubscribedToEventType() throws Exception { + DomainEventPublisher.instance().subscribe(new DomainEventSubscriber() { + @Override + public void handleEvent(TestableDomainEvent aDomainEvent) { + eventHandled = true; + } + + @Override + public Class subscribedToEventType() { + return TestableDomainEvent.class; + } + }); + + assertFalse(this.eventHandled); + + DomainEventPublisher.instance().publish(new AKindOfTestableDomainEvent(100L, "test", 1)); + + assertTrue(this.eventHandled); } }