From 816c891269cedc35d39622a65412ef6b1e751a0b Mon Sep 17 00:00:00 2001 From: smcvb Date: Wed, 12 Sep 2018 12:24:29 +0200 Subject: [PATCH] Add Builder pattern for SimpleEventScheduler -Add Builder pattern for SimpleEventScheduler -Change usages of constructor to builder -Reindent touched files #754 --- .../scheduling/java/SimpleEventScheduler.java | 135 ++++++++++++++---- .../java/SimpleEventSchedulerTest.java | 32 +++-- .../java/SimpleEventSchedulerFactoryBean.java | 16 ++- 3 files changed, 135 insertions(+), 48 deletions(-) diff --git a/core/src/main/java/org/axonframework/eventhandling/scheduling/java/SimpleEventScheduler.java b/core/src/main/java/org/axonframework/eventhandling/scheduling/java/SimpleEventScheduler.java index 6e9a575c09..2d31c066ca 100644 --- a/core/src/main/java/org/axonframework/eventhandling/scheduling/java/SimpleEventScheduler.java +++ b/core/src/main/java/org/axonframework/eventhandling/scheduling/java/SimpleEventScheduler.java @@ -16,7 +16,7 @@ package org.axonframework.eventhandling.scheduling.java; -import org.axonframework.common.Assert; +import org.axonframework.common.AxonConfigurationException; import org.axonframework.common.IdentifierFactory; import org.axonframework.common.transaction.NoTransactionManager; import org.axonframework.common.transaction.TransactionManager; @@ -34,7 +34,13 @@ import java.time.Duration; import java.time.Instant; import java.util.Map; -import java.util.concurrent.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import static org.axonframework.common.BuilderUtils.assertNonNull; /** * An {@link EventScheduler} implementation that uses Java's ScheduledExecutorService as scheduling and triggering @@ -52,40 +58,38 @@ public class SimpleEventScheduler implements EventScheduler { private static final Logger logger = LoggerFactory.getLogger(SimpleEventScheduler.class); - private final ScheduledExecutorService executorService; + private final ScheduledExecutorService scheduledExecutorService; private final EventBus eventBus; private final TransactionManager transactionManager; + private final Map> tokens = new ConcurrentHashMap<>(); /** - * Initialize the SimpleEventScheduler using the given {@code executorService} as trigger and execution - * mechanism, and publishes events to the given {@code eventBus}. + * Instantiate a {@link SimpleEventScheduler} based on the fields contained in the {@link Builder}. + *

+ * Will assert that the {@link ScheduledExecutorService}, {@link EventBus} and {@link TransactionManager} are not + * {@code null}, and will throw an {@link AxonConfigurationException} if any of them is {@code null}. * - * @param executorService The backing ScheduledExecutorService - * @param eventBus The Event Bus on which Events are to be published + * @param builder the {@link Builder} used to instantiate a {@link SimpleEventScheduler} instance */ - public SimpleEventScheduler(ScheduledExecutorService executorService, EventBus eventBus) { - this(executorService, eventBus, NoTransactionManager.INSTANCE); + protected SimpleEventScheduler(Builder builder) { + builder.validate(); + this.scheduledExecutorService = builder.scheduledExecutorService; + this.eventBus = builder.eventBus; + this.transactionManager = builder.transactionManager; } /** - * Initialize the SimpleEventScheduler using the given {@code executorService} as trigger and execution - * mechanism, and publishes events to the given {@code eventBus}. The {@code eventTriggerCallback} is - * invoked just before and after publication of a scheduled event. + * Builder class to instantiate a {@link SimpleEventScheduler}. + *

+ * The {@link TransactionManager} is defaulted to a {@link NoTransactionManager}. The + * {@link ScheduledExecutorService} and {@link EventBus} are a hard requirements and as such should be + * provided. * - * @param executorService The backing ScheduledExecutorService - * @param eventBus The Event Bus on which Events are to be published - * @param transactionManager to manage the transaction around Event publication + * @return a Builder to be able to create a {@link SimpleEventScheduler} */ - public SimpleEventScheduler(ScheduledExecutorService executorService, EventBus eventBus, - TransactionManager transactionManager) { - Assert.notNull(executorService, () -> "executorService may not be null"); - Assert.notNull(eventBus, () -> "eventBus may not be null"); - Assert.notNull(transactionManager, () -> "transactionManager may not be null"); - - this.executorService = executorService; - this.eventBus = eventBus; - this.transactionManager = transactionManager; + public static Builder builder() { + return new Builder(); } @Override @@ -96,9 +100,9 @@ public ScheduleToken schedule(Instant triggerDateTime, Object event) { @Override public ScheduleToken schedule(Duration triggerDuration, Object event) { String tokenId = IdentifierFactory.getInstance().generateIdentifier(); - ScheduledFuture future = executorService.schedule(new PublishEventTask(event, tokenId), - triggerDuration.toMillis(), - TimeUnit.MILLISECONDS); + ScheduledFuture future = scheduledExecutorService.schedule(new PublishEventTask(event, tokenId), + triggerDuration.toMillis(), + TimeUnit.MILLISECONDS); tokens.put(tokenId, future); return new SimpleScheduleToken(tokenId); } @@ -150,11 +154,86 @@ private EventMessage createMessage() { EventMessage eventMessage; if (event instanceof EventMessage) { eventMessage = new GenericEventMessage<>(((EventMessage) event).getPayload(), - ((EventMessage) event).getMetaData()); + ((EventMessage) event).getMetaData()); } else { eventMessage = new GenericEventMessage<>(event, MetaData.emptyInstance()); } return eventMessage; } } + + /** + * Builder class to instantiate a {@link SimpleEventScheduler}. + *

+ * The {@link TransactionManager} is defaulted to a {@link NoTransactionManager}. The + * {@link ScheduledExecutorService} and {@link EventBus} are a hard requirements and as such should be + * provided. + */ + public static class Builder { + + private ScheduledExecutorService scheduledExecutorService; + private EventBus eventBus; + private TransactionManager transactionManager = NoTransactionManager.INSTANCE; + + /** + * Sets the {@link EventBus} used to publish scheduled events on once the schedule has been met. + * + * @param eventBus a {@link EventBus} used to publish scheduled events on once the schedule has been met + * @return the current Builder instance, for a fluent interfacing + */ + public Builder eventBus(EventBus eventBus) { + assertNonNull(eventBus, "EventBus may not be null"); + this.eventBus = eventBus; + return this; + } + + /** + * Sets the {@link ScheduledExecutorService} used for scheduling and triggering events. + * + * @param scheduledExecutorService a {@link ScheduledExecutorService} used for scheduling and triggering + * events + * @return the current Builder instance, for a fluent interfacing + */ + public Builder scheduledExecutorService(ScheduledExecutorService scheduledExecutorService) { + assertNonNull(scheduledExecutorService, "ScheduledExecutorService may not be null"); + this.scheduledExecutorService = scheduledExecutorService; + return this; + } + + /** + * Sets the {@link TransactionManager} used to build transactions and ties them on event publication. Defaults + * to a {@link NoTransactionManager}. + * + * @param transactionManager a {@link TransactionManager} used to build transactions and ties them on event + * publication + * @return the current Builder instance, for a fluent interfacing + */ + public Builder transactionManager(TransactionManager transactionManager) { + assertNonNull(transactionManager, "TransactionManager may not be null"); + this.transactionManager = transactionManager; + return this; + } + + /** + * Initializes a {@link SimpleEventScheduler} as specified through this Builder. + * + * @return a {@link SimpleEventScheduler} as specified through this Builder + */ + public SimpleEventScheduler build() { + return new SimpleEventScheduler(this); + } + + /** + * Validate whether the fields contained in this Builder are set accordingly. + * + * @throws AxonConfigurationException if one field is asserted to be incorrect according to the Builder's + * specifications + */ + protected void validate() throws AxonConfigurationException { + assertNonNull(eventBus, "The EventBus is a hard requirement and should be provided"); + assertNonNull(scheduledExecutorService, + "The ScheduledExecutorService is a hard requirement and should be provided"); + assertNonNull(transactionManager, "The TransactionManager is a hard requirement and should be provided"); + } + } } diff --git a/core/src/test/java/org/axonframework/eventhandling/scheduling/java/SimpleEventSchedulerTest.java b/core/src/test/java/org/axonframework/eventhandling/scheduling/java/SimpleEventSchedulerTest.java index c68480bbcf..160e865929 100644 --- a/core/src/test/java/org/axonframework/eventhandling/scheduling/java/SimpleEventSchedulerTest.java +++ b/core/src/test/java/org/axonframework/eventhandling/scheduling/java/SimpleEventSchedulerTest.java @@ -21,13 +21,14 @@ import org.axonframework.eventhandling.GenericEventMessage; import org.axonframework.eventhandling.saga.Saga; import org.axonframework.eventhandling.scheduling.ScheduleToken; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentMatcher; -import org.quartz.SchedulerException; +import org.junit.*; +import org.mockito.*; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.time.Duration; import java.util.UUID; import java.util.concurrent.CountDownLatch; @@ -35,7 +36,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import static org.mockito.Mockito.*; /** @@ -46,19 +47,22 @@ public class SimpleEventSchedulerTest { private SimpleEventScheduler testSubject; private EventBus eventBus; - private ScheduledExecutorService executorService; + private ScheduledExecutorService scheduledExecutorService; @Before public void setUp() { eventBus = mock(EventBus.class); - executorService = Executors.newSingleThreadScheduledExecutor(); - testSubject = new SimpleEventScheduler(executorService, eventBus); + scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); + testSubject = SimpleEventScheduler.builder() + .scheduledExecutorService(scheduledExecutorService) + .eventBus(eventBus) + .build(); } @After public void tearDown() { - if (executorService != null) { - executorService.shutdownNow(); + if (scheduledExecutorService != null) { + scheduledExecutorService.shutdownNow(); } } @@ -107,9 +111,9 @@ public void testCancelJob() throws InterruptedException { verify(eventBus).publish(argThat((ArgumentMatcher) item -> (item != null) && event2.getPayload().equals(item.getPayload()) && event2.getMetaData().equals(item.getMetaData()))); - executorService.shutdown(); + scheduledExecutorService.shutdown(); assertTrue("Executor refused to shutdown within a second", - executorService.awaitTermination(1, TimeUnit.SECONDS)); + scheduledExecutorService.awaitTermination(1, TimeUnit.SECONDS)); } private EventMessage createEvent() { diff --git a/spring/src/main/java/org/axonframework/spring/eventhandling/scheduling/java/SimpleEventSchedulerFactoryBean.java b/spring/src/main/java/org/axonframework/spring/eventhandling/scheduling/java/SimpleEventSchedulerFactoryBean.java index 4c4090d97c..b5c6233a4f 100644 --- a/spring/src/main/java/org/axonframework/spring/eventhandling/scheduling/java/SimpleEventSchedulerFactoryBean.java +++ b/spring/src/main/java/org/axonframework/spring/eventhandling/scheduling/java/SimpleEventSchedulerFactoryBean.java @@ -78,13 +78,17 @@ public void afterPropertiesSet() { if (eventBus == null) { eventBus = applicationContext.getBean(EventBus.class); } - if (transactionManager == null) { - this.eventScheduler = new SimpleEventScheduler(executorService, eventBus); - } else { - this.eventScheduler = new SimpleEventScheduler( - executorService, eventBus, - new SpringTransactionManager(transactionManager, transactionDefinition)); + + SimpleEventScheduler.Builder eventSchedulerBuilder = + SimpleEventScheduler.builder() + .scheduledExecutorService(executorService) + .eventBus(eventBus); + if (transactionManager != null) { + eventSchedulerBuilder.transactionManager( + new SpringTransactionManager(transactionManager, transactionDefinition) + ); } + this.eventScheduler = eventSchedulerBuilder.build(); } @Override