Skip to content

Commit 303a680

Browse files
committed
GH-206 - Event Publication Registry now uses custom Clock instance if configured.
We now consider a user defined Clock bean in the application context to obtain the Instant to use as publication date for event publications.
1 parent 77bfc48 commit 303a680

File tree

7 files changed

+122
-13
lines changed

7 files changed

+122
-13
lines changed

spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/CompletableEventPublication.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.modulith.events;
1717

18+
import java.time.Clock;
1819
import java.time.Instant;
1920
import java.util.Optional;
2021

@@ -49,13 +50,28 @@ default boolean isPublicationCompleted() {
4950
CompletableEventPublication markCompleted();
5051

5152
/**
52-
* Creates a {@link CompletableEventPublication} for the given event an listener identifier.
53+
* Creates a {@link CompletableEventPublication} for the given event an listener identifier using a default
54+
* {@link Instant}. Prefer using {@link #of(Object, PublicationTargetIdentifier, Instant)} with a dedicated
55+
* {@link Instant} obtained from a {@link Clock}.
5356
*
5457
* @param event must not be {@literal null}.
5558
* @param id must not be {@literal null}.
56-
* @return
59+
* @return will never be {@literal null}.
60+
* @see #of(Object, PublicationTargetIdentifier, Instant)
5761
*/
5862
static CompletableEventPublication of(Object event, PublicationTargetIdentifier id) {
59-
return new DefaultEventPublication(event, id);
63+
return new DefaultEventPublication(event, id, Instant.now());
64+
}
65+
66+
/**
67+
* Creates a {@link CompletableEventPublication} for the given event an listener identifier and publication date.
68+
*
69+
* @param event must not be {@literal null}.
70+
* @param id must not be {@literal null}.
71+
* @param publicationDate must not be {@literal null}.
72+
* @return will never be {@literal null}.
73+
*/
74+
static CompletableEventPublication of(Object event, PublicationTargetIdentifier id, Instant publicationDate) {
75+
return new DefaultEventPublication(event, id, publicationDate);
6076
}
6177
}

spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/DefaultEventPublication.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,17 @@ class DefaultEventPublication implements CompletableEventPublication {
3939
*
4040
* @param event must not be {@literal null}.
4141
* @param targetIdentifier must not be {@literal null}.
42+
* @param publicationDate must not be {@literal null}.
4243
*/
43-
DefaultEventPublication(Object event, PublicationTargetIdentifier targetIdentifier) {
44+
DefaultEventPublication(Object event, PublicationTargetIdentifier targetIdentifier, Instant publicationDate) {
4445

4546
Assert.notNull(event, "Event must not be null!");
4647
Assert.notNull(targetIdentifier, "PublicationTargetIdentifier must not be null!");
48+
Assert.notNull(publicationDate, "Publication date must not be null!");
4749

4850
this.event = event;
4951
this.targetIdentifier = targetIdentifier;
50-
this.publicationDate = Instant.now();
52+
this.publicationDate = publicationDate;
5153
this.completionDate = Optional.empty();
5254
}
5355

spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/DefaultEventPublicationRegistry.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.modulith.events;
1717

18+
import java.time.Clock;
1819
import java.util.Collection;
1920
import java.util.List;
2021
import java.util.stream.Stream;
@@ -40,28 +41,33 @@ public class DefaultEventPublicationRegistry implements DisposableBean, EventPub
4041
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultEventPublicationRegistry.class);
4142

4243
private final EventPublicationRepository events;
44+
private final Clock clock;
4345

4446
/**
4547
* Creates a new {@link DefaultEventPublicationRegistry} for the given {@link EventPublicationRepository}.
4648
*
4749
* @param events must not be {@literal null}.
50+
* @param clock must not be {@literal null}.
4851
*/
49-
public DefaultEventPublicationRegistry(EventPublicationRepository events) {
52+
public DefaultEventPublicationRegistry(EventPublicationRepository events, Clock clock) {
5053

5154
Assert.notNull(events, "EventPublicationRepository must not be null!");
55+
Assert.notNull(clock, "Clock must not be null!");
5256

5357
this.events = events;
58+
this.clock = clock;
5459
}
5560

5661
/*
5762
* (non-Javadoc)
5863
* @see org.springframework.modulith.events.EventPublicationRegistry#store(java.lang.Object, java.util.stream.Stream)
5964
*/
6065
@Override
61-
public void store(Object event, Stream<PublicationTargetIdentifier> listeners) {
66+
public Collection<EventPublication> store(Object event, Stream<PublicationTargetIdentifier> listeners) {
6267

63-
listeners.map(it -> map(event, it))
64-
.forEach(events::create);
68+
return listeners.map(it -> map(event, it))
69+
.map(events::create)
70+
.toList();
6571
}
6672

6773
/*
@@ -118,7 +124,7 @@ public void destroy() {
118124

119125
private EventPublication map(Object event, PublicationTargetIdentifier targetIdentifier) {
120126

121-
EventPublication result = CompletableEventPublication.of(event, targetIdentifier);
127+
var result = CompletableEventPublication.of(event, targetIdentifier, clock.instant());
122128

123129
LOGGER.debug("Registering publication of {} for {}.", //
124130
result.getEvent().getClass().getName(), result.getTargetIdentifier().getValue());

spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/EventPublicationRegistry.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public interface EventPublicationRegistry {
3636
* @param event must not be {@literal null}.
3737
* @param listeners must not be {@literal null}.
3838
*/
39-
void store(Object event, Stream<PublicationTargetIdentifier> listeners);
39+
Collection<EventPublication> store(Object event, Stream<PublicationTargetIdentifier> listeners);
4040

4141
/**
4242
* Returns all {@link EventPublication}s that have not been completed yet.

spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/config/EventPublicationConfiguration.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
*/
1616
package org.springframework.modulith.events.config;
1717

18+
import java.time.Clock;
1819
import java.time.Duration;
1920
import java.util.Arrays;
2021

2122
import org.slf4j.Logger;
2223
import org.slf4j.LoggerFactory;
2324
import org.springframework.beans.BeansException;
2425
import org.springframework.beans.factory.ObjectFactory;
26+
import org.springframework.beans.factory.ObjectProvider;
2527
import org.springframework.beans.factory.config.BeanDefinition;
2628
import org.springframework.beans.factory.config.BeanPostProcessor;
2729
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -55,8 +57,9 @@ class EventPublicationConfiguration {
5557

5658
@Bean
5759
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
58-
EventPublicationRegistry eventPublicationRegistry(EventPublicationRepository repository) {
59-
return new DefaultEventPublicationRegistry(repository);
60+
EventPublicationRegistry eventPublicationRegistry(EventPublicationRepository repository,
61+
ObjectProvider<Clock> clock) {
62+
return new DefaultEventPublicationRegistry(repository, clock.getIfAvailable(() -> Clock.systemUTC()));
6063
}
6164

6265
@Bean
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.events;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
import static org.mockito.AdditionalAnswers.*;
20+
import static org.mockito.ArgumentMatchers.*;
21+
import static org.mockito.Mockito.*;
22+
23+
import java.time.Clock;
24+
import java.time.Instant;
25+
import java.time.ZoneId;
26+
import java.util.stream.Stream;
27+
28+
import org.junit.jupiter.api.Test;
29+
import org.junit.jupiter.api.extension.ExtendWith;
30+
import org.mockito.Mock;
31+
import org.mockito.junit.jupiter.MockitoExtension;
32+
33+
/**
34+
* Unit tests for {@link DefaultEventPublicationRegistry}.
35+
*
36+
* @author Oliver Drotbohm
37+
*/
38+
@ExtendWith(MockitoExtension.class)
39+
class DefaultEventPublicationRegistryUnitTests {
40+
41+
@Mock EventPublicationRepository repository;
42+
@Mock Clock clock;
43+
44+
@Test // GH-206
45+
void usesCustomClockIfConfigured() {
46+
47+
when(repository.create(any())).then(returnsFirstArg());
48+
49+
var now = Instant.now();
50+
var clock = Clock.fixed(now, ZoneId.systemDefault());
51+
52+
var registry = new DefaultEventPublicationRegistry(repository, clock);
53+
54+
var identifier = PublicationTargetIdentifier.of("id");
55+
var publications = registry.store(new Object(), Stream.of(identifier));
56+
57+
assertThat(publications).hasSize(1).element(0).satisfies(it -> {
58+
assertThat(it.getPublicationDate()).isEqualTo(now);
59+
assertThat(it.getTargetIdentifier()).isEqualTo(identifier);
60+
});
61+
}
62+
}

spring-modulith-events/spring-modulith-events-core/src/test/java/org/springframework/modulith/events/config/EventPublicationConfigurationIntegrationTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717

1818
import static org.assertj.core.api.Assertions.*;
1919

20+
import java.time.Clock;
2021
import java.time.Duration;
22+
import java.time.Instant;
23+
import java.time.ZoneId;
2124
import java.util.function.Function;
2225

2326
import org.junit.jupiter.api.Test;
@@ -32,11 +35,13 @@
3235
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
3336
import org.springframework.boot.test.context.runner.ContextConsumer;
3437
import org.springframework.context.annotation.AdviceMode;
38+
import org.springframework.modulith.events.EventPublicationRegistry;
3539
import org.springframework.modulith.events.EventPublicationRepository;
3640
import org.springframework.modulith.events.config.EventPublicationConfiguration.AsyncPropertiesDefaulter;
3741
import org.springframework.scheduling.annotation.EnableAsync;
3842
import org.springframework.scheduling.annotation.ProxyAsyncConfiguration;
3943
import org.springframework.scheduling.aspectj.AspectJAsyncConfiguration;
44+
import org.springframework.test.util.ReflectionTestUtils;
4045

4146
/**
4247
* Unit tests for {@link EventPublicationConfiguration}.
@@ -105,6 +110,21 @@ void doesNotEnableAsyncSupportByDefaultIfExplicitlyConfigured() {
105110
});
106111
}
107112

113+
@Test // GH-206
114+
void wiresCustomClockIntoEventPublicationRegistryIfConfigured() {
115+
116+
var clock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
117+
118+
basicSetup()
119+
.withBean(Clock.class, () -> clock)
120+
.run(context -> {
121+
122+
var registry = context.getBean(EventPublicationRegistry.class);
123+
124+
assertThat(ReflectionTestUtils.getField(registry, "clock")).isSameAs(clock);
125+
});
126+
}
127+
108128
private <T> ContextConsumer<AssertableApplicationContext> expect(Function<Shutdown, T> extractor,
109129
T expected) {
110130

0 commit comments

Comments
 (0)