Skip to content

Commit

Permalink
Properly emit domain events from calls to saveAll(…).
Browse files Browse the repository at this point in the history
We now treat CrudRepository.saveAll(…) properly by unwrapping the given *Iterable*. This previously already worked for collections handed into the method but not for types only implementing Iterable directly (like Page or Window).

Fixes #3153.
Related tickets #2931, #2927.
  • Loading branch information
odrotbohm committed Sep 20, 2023
1 parent 9b2e898 commit 5f64564
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,15 @@ public class EventPublishingRepositoryProxyPostProcessor implements RepositoryPr

private final ApplicationEventPublisher publisher;

/**
* Creates a new {@link EventPublishingRepositoryProxyPostProcessor} for the given {@link ApplicationEventPublisher}.
*
* @param publisher must not be {@literal null}.
*/
public EventPublishingRepositoryProxyPostProcessor(ApplicationEventPublisher publisher) {

Assert.notNull(publisher, "Object must not be null");

this.publisher = publisher;
}

Expand Down Expand Up @@ -103,9 +111,9 @@ public Object invoke(MethodInvocation invocation) throws Throwable {
return result;
}

Object[] arguments = invocation.getArguments();
Iterable<?> arguments = asCollection(invocation.getArguments()[0], invocation.getMethod());

eventMethod.publishEventsFrom(arguments[0], publisher);
eventMethod.publishEventsFrom(arguments, publisher);

return result;
}
Expand Down Expand Up @@ -177,22 +185,18 @@ public static EventPublishingMethod of(Class<?> type) {
/**
* Publishes all events in the given aggregate root using the given {@link ApplicationEventPublisher}.
*
* @param object can be {@literal null}.
* @param aggregates can be {@literal null}.
* @param publisher must not be {@literal null}.
*/
public void publishEventsFrom(@Nullable Object object, ApplicationEventPublisher publisher) {
public void publishEventsFrom(Iterable<?> aggregates, ApplicationEventPublisher publisher) {

if (object == null) {
return;
}

for (Object aggregateRoot : asCollection(object)) {
for (Object aggregateRoot : aggregates) {

if (!type.isInstance(aggregateRoot)) {
continue;
}

for (Object event : asCollection(ReflectionUtils.invokeMethod(publishingMethod, aggregateRoot))) {
for (Object event : asCollection(ReflectionUtils.invokeMethod(publishingMethod, aggregateRoot), null)) {
publisher.publishEvent(event);
}

Expand Down Expand Up @@ -261,25 +265,30 @@ private static Method getClearingMethod(AnnotationDetectionMethodCallback<?> cle
return method;
}

/**
* Returns the given source object as collection, i.e. collections are returned as is, objects are turned into a
* one-element collection, {@literal null} will become an empty collection.
*
* @param source can be {@literal null}.
* @return
*/
@SuppressWarnings("unchecked")
private static Collection<Object> asCollection(@Nullable Object source) {
}

if (source == null) {
return Collections.emptyList();
}
/**
* Returns the given source object as collection, i.e. collections are returned as is, objects are turned into a
* one-element collection, {@literal null} will become an empty collection.
*
* @param source can be {@literal null}.
* @return
*/
@SuppressWarnings("unchecked")
private static Iterable<Object> asCollection(@Nullable Object source, @Nullable Method method) {

if (Collection.class.isInstance(source)) {
return (Collection<Object>) source;
}
if (source == null) {
return Collections.emptyList();
}

return Collections.singletonList(source);
if (method != null && method.getName().startsWith("saveAll")) {
return (Iterable<Object>) source;
}

if (Collection.class.isInstance(source)) {
return (Collection<Object>) source;
}

return Collections.singletonList(source);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,14 @@ void rejectsNullAggregateTypes() {
assertThatIllegalArgumentException().isThrownBy(() -> EventPublishingMethod.of(null));
}

@Test // DATACMNS-928
void publishingEventsForNullIsNoOp() {
EventPublishingMethod.of(OneEvent.class).publishEventsFrom(null, publisher);
}

@Test // DATACMNS-928
void exposesEventsExposedByEntityToPublisher() {

var first = new SomeEvent();
var second = new SomeEvent();
var entity = MultipleEvents.of(Arrays.asList(first, second));

EventPublishingMethod.of(MultipleEvents.class).publishEventsFrom(entity, publisher);
EventPublishingMethod.of(MultipleEvents.class).publishEventsFrom(List.of(entity), publisher);

verify(publisher).publishEvent(eq(first));
verify(publisher).publishEvent(eq(second));
Expand All @@ -88,7 +83,7 @@ void exposesSingleEventByEntityToPublisher() {
var event = new SomeEvent();
var entity = OneEvent.of(event);

EventPublishingMethod.of(OneEvent.class).publishEventsFrom(entity, publisher);
EventPublishingMethod.of(OneEvent.class).publishEventsFrom(List.of(entity), publisher);

verify(publisher, times(1)).publishEvent(event);
}
Expand All @@ -98,7 +93,7 @@ void doesNotExposeNullEvent() {

var entity = OneEvent.of(null);

EventPublishingMethod.of(OneEvent.class).publishEventsFrom(entity, publisher);
EventPublishingMethod.of(OneEvent.class).publishEventsFrom(List.of(entity), publisher);

verify(publisher, times(0)).publishEvent(any());
}
Expand Down Expand Up @@ -274,7 +269,7 @@ void clearsEventsEvenIfNoneWereExposedToPublish() {

var entity = spy(EventsWithClearing.of(Collections.emptyList()));

EventPublishingMethod.of(EventsWithClearing.class).publishEventsFrom(entity, publisher);
EventPublishingMethod.of(EventsWithClearing.class).publishEventsFrom(List.of(entity), publisher);

verify(entity, times(1)).clearDomainEvents();
}
Expand All @@ -284,7 +279,7 @@ void clearsEventsIfThereWereSomeToBePublished() {

var entity = spy(EventsWithClearing.of(Collections.singletonList(new SomeEvent())));

EventPublishingMethod.of(EventsWithClearing.class).publishEventsFrom(entity, publisher);
EventPublishingMethod.of(EventsWithClearing.class).publishEventsFrom(List.of(entity), publisher);

verify(entity, times(1)).clearDomainEvents();
}
Expand Down

0 comments on commit 5f64564

Please sign in to comment.