diff --git a/spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/config/EventPublicationConfiguration.java b/spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/config/EventPublicationConfiguration.java index 59d89f71..a50919b1 100644 --- a/spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/config/EventPublicationConfiguration.java +++ b/spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/config/EventPublicationConfiguration.java @@ -62,8 +62,10 @@ class EventPublicationConfiguration { @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) static PersistentApplicationEventMulticaster applicationEventMulticaster( - ObjectFactory eventPublicationRegistry) { - return new PersistentApplicationEventMulticaster(() -> eventPublicationRegistry.getObject()); + ObjectFactory eventPublicationRegistry, ObjectFactory environment) { + + return new PersistentApplicationEventMulticaster(() -> eventPublicationRegistry.getObject(), + () -> environment.getObject()); } @Bean diff --git a/spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/support/PersistentApplicationEventMulticaster.java b/spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/support/PersistentApplicationEventMulticaster.java index 2d04f64a..cf9e3349 100644 --- a/spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/support/PersistentApplicationEventMulticaster.java +++ b/spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/support/PersistentApplicationEventMulticaster.java @@ -33,6 +33,7 @@ import org.springframework.context.event.ApplicationEventMulticaster; import org.springframework.context.event.ApplicationListenerMethodAdapter; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.core.env.Environment; import org.springframework.lang.NonNull; import org.springframework.modulith.events.EventPublication; import org.springframework.modulith.events.EventPublicationRegistry; @@ -60,8 +61,10 @@ public class PersistentApplicationEventMulticaster extends AbstractApplicationEv private static final Logger LOGGER = LoggerFactory.getLogger(PersistentApplicationEventMulticaster.class); private static final Field DECLARED_EVENT_TYPES_FIELD = ReflectionUtils .findField(ApplicationListenerMethodAdapter.class, "declaredEventTypes"); + static final String REPUBLISH_ON_RESTART = "spring.modulith.republish-outstanding-events-on-restart"; private final @NonNull Supplier registry; + private final @NonNull Supplier environment; static { ReflectionUtils.makeAccessible(DECLARED_EVENT_TYPES_FIELD); @@ -71,12 +74,16 @@ public class PersistentApplicationEventMulticaster extends AbstractApplicationEv * Creates a new {@link PersistentApplicationEventMulticaster} for the given {@link EventPublicationRegistry}. * * @param registry must not be {@literal null}. + * @param environment must not be {@literal null}. */ - public PersistentApplicationEventMulticaster(Supplier registry) { + public PersistentApplicationEventMulticaster(Supplier registry, + Supplier environment) { Assert.notNull(registry, "EventPublicationRegistry must not be null!"); + Assert.notNull(environment, "Environment must not be null!"); this.registry = registry; + this.environment = environment; } /* @@ -118,6 +125,10 @@ public class PersistentApplicationEventMulticaster extends AbstractApplicationEv @Override public void afterSingletonsInstantiated() { + if (Boolean.FALSE.equals(environment.get().getProperty(REPUBLISH_ON_RESTART, Boolean.class))) { + return; + } + LOGGER.debug("Looking up previously pending event publications…"); var publications = registry.get().findIncompletePublications(); diff --git a/spring-modulith-events/spring-modulith-events-core/src/main/resources/META-INF/spring-configuration-metadata.json b/spring-modulith-events/spring-modulith-events-core/src/main/resources/META-INF/spring-configuration-metadata.json index 517150cd..fce45396 100644 --- a/spring-modulith-events/spring-modulith-events-core/src/main/resources/META-INF/spring-configuration-metadata.json +++ b/spring-modulith-events/spring-modulith-events-core/src/main/resources/META-INF/spring-configuration-metadata.json @@ -5,6 +5,12 @@ "type": "java.lang.boolean", "description": "Whether to configure defaults for the async processing termination, namely to wait for task completion for 2 seconds. See TaskExecutionProperties for details.", "defaultValue": "true" + }, + { + "name": "spring.modulith.republish-outstanding-events-on-restart", + "type": "java.lang.boolean", + "description": "Whether to republish outstanding event publications on restarts of the application.", + "defaultValue": "true" } ] } diff --git a/spring-modulith-events/spring-modulith-events-core/src/test/java/org/springframework/modulith/events/support/PersistentApplicationEventMulticasterUnitTests.java b/spring-modulith-events/spring-modulith-events-core/src/test/java/org/springframework/modulith/events/support/PersistentApplicationEventMulticasterUnitTests.java new file mode 100644 index 00000000..5ce8af2b --- /dev/null +++ b/spring-modulith-events/spring-modulith-events-core/src/test/java/org/springframework/modulith/events/support/PersistentApplicationEventMulticasterUnitTests.java @@ -0,0 +1,64 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.modulith.events.support; + +import static org.mockito.Mockito.*; + +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.modulith.events.EventPublicationRegistry; + +/** + * Unit tests for {@link PersistentApplicationEventMulticaster}. + * + * @author Oliver Drotbohm + */ +class PersistentApplicationEventMulticasterUnitTests { + + PersistentApplicationEventMulticaster multicaster; + + StandardEnvironment environment = new StandardEnvironment(); + EventPublicationRegistry registry = mock(EventPublicationRegistry.class); + + @BeforeEach + void setUp() { + this.multicaster = new PersistentApplicationEventMulticaster(() -> registry, () -> environment); + } + + @Test // GH-240 + void doesNotRepublishEventsOnRestartIfExplicitlyDisabled() { + + var source = new MapPropertySource("test", + Map.of(PersistentApplicationEventMulticaster.REPUBLISH_ON_RESTART, "false")); + environment.getPropertySources().addFirst(source); + + multicaster.afterSingletonsInstantiated(); + + verify(registry, never()).findIncompletePublications(); + } + + @Test // GH-240 + void triggersRepublicationIfExplicitlyEnabled() { + + multicaster.afterSingletonsInstantiated(); + + verify(registry).findIncompletePublications(); + } +}