Skip to content

Commit

Permalink
Use spring-boot event to start workers
Browse files Browse the repository at this point in the history
Users no longer need to invoke .start() on SpringApplication.  Fixes temporalio#1837
  • Loading branch information
osialr committed Aug 7, 2023
1 parent 5ce6f10 commit 2540b7b
Show file tree
Hide file tree
Showing 11 changed files with 43 additions and 85 deletions.
6 changes: 0 additions & 6 deletions temporal-spring-boot-autoconfigure-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,9 @@ This allows to wire `TestWorkflowEnvironment` bean in your unit tests:
@SpringBootTest(classes = Test.Configuration.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class Test {
@Autowired ConfigurableApplicationContext applicationContext;
@Autowired TestWorkflowEnvironment testWorkflowEnvironment;
@Autowired WorkflowClient workflowClient;
@BeforeEach
void setUp() {
applicationContext.start();
}
@Test
@Timeout(value = 10)
public void test() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.event.ContextStartedEvent;

@Configuration
@EnableConfigurationProperties(TemporalProperties.class)
Expand Down Expand Up @@ -158,23 +158,23 @@ public WorkerFactoryStarter workerFactoryStarter(WorkerFactory workerFactory) {
return new WorkerFactoryStarter(workerFactory);
}

// It needs to listed on ContextStartedEvent, not ContextRefreshedEvent.
// It needs to listen on ApplicationReadyEvent, not ContextRefreshedEvent.
// Using ContextRefreshedEvent will cause start of workers early, during the context
// initialization
// and potentially incorrect order of bean initialization.
// Refresh event can also be fired multiple times during the context initialization.
// For this listener to ever work, Spring context needs to be actually started.
// Note that a lot of online samples for Spring don't start the context at all:
// https://stackoverflow.com/questions/48099355/contextstartedevent-not-firing-in-custom-listener
public static class WorkerFactoryStarter implements ApplicationListener<ContextStartedEvent> {
// See spring-boot Application Events and listeners:
// https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.spring-application.application-events-and-listeners
public static class WorkerFactoryStarter implements ApplicationListener<ApplicationReadyEvent> {
private final WorkerFactory workerFactory;

public WorkerFactoryStarter(WorkerFactory workerFactory) {
this.workerFactory = workerFactory;
}

@Override
public void onApplicationEvent(@Nonnull ContextStartedEvent event) {
public void onApplicationEvent(@Nonnull ApplicationReadyEvent event) {
workerFactory.start();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@
import io.temporal.client.WorkflowOptions;
import io.temporal.spring.boot.autoconfigure.bytaskqueue.TestWorkflow;
import io.temporal.worker.WorkerFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.test.context.ActiveProfiles;
Expand All @@ -39,17 +37,10 @@
@ActiveProfiles(profiles = "auto-discovery-by-task-queue-dynamic-suffix")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class AutoDiscoveryByTaskQueueResolverTest {
@Autowired ConfigurableApplicationContext applicationContext;

@Autowired WorkflowClient workflowClient;

@Autowired WorkerFactory workerFactory;

@BeforeEach
void setUp() {
applicationContext.start();
}

@Test
@Timeout(value = 10)
public void testAutoDiscovery() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@
import io.temporal.client.WorkflowOptions;
import io.temporal.spring.boot.autoconfigure.bytaskqueue.TestWorkflow;
import io.temporal.testing.TestWorkflowEnvironment;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.test.context.ActiveProfiles;
Expand All @@ -39,17 +37,10 @@
@ActiveProfiles(profiles = "auto-discovery-by-task-queue")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class AutoDiscoveryByTaskQueueTest {
@Autowired ConfigurableApplicationContext applicationContext;

@Autowired TestWorkflowEnvironment testWorkflowEnvironment;

@Autowired WorkflowClient workflowClient;

@BeforeEach
void setUp() {
applicationContext.start();
}

@Test
@Timeout(value = 10)
public void testAutoDiscovery() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@
import io.temporal.client.WorkflowOptions;
import io.temporal.spring.boot.autoconfigure.bytaskqueue.TestWorkflow;
import io.temporal.testing.TestWorkflowEnvironment;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.test.context.ActiveProfiles;
Expand All @@ -39,17 +37,10 @@
@ActiveProfiles(profiles = "auto-discovery-by-worker-name")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class AutoDiscoveryByWorkerNameTest {
@Autowired ConfigurableApplicationContext applicationContext;

@Autowired TestWorkflowEnvironment testWorkflowEnvironment;

@Autowired WorkflowClient workflowClient;

@BeforeEach
void setUp() {
applicationContext.start();
}

@Test
@Timeout(value = 10)
public void testAutoDiscovery() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import io.temporal.client.WorkflowClient;
import io.temporal.client.schedules.ScheduleClient;
import io.temporal.testing.TestWorkflowEnvironment;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.Timeout;
Expand All @@ -48,11 +47,6 @@ public class ClientOnlyTest {

@Autowired ScheduleClient scheduleClient;

@BeforeEach
void setUp() {
applicationContext.start();
}

@Test
@Timeout(value = 10)
public void testClientWiring() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,20 @@
import static org.junit.jupiter.api.Assertions.assertEquals;

import io.temporal.client.WorkflowClient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ActiveProfiles;

@SpringBootTest(classes = CustomClientConfigTest.Configuration.class)
@ActiveProfiles(profiles = "custom-namespace")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class CustomClientConfigTest {
@Autowired ConfigurableApplicationContext applicationContext;

@Autowired protected WorkflowClient workflowClient;

@BeforeEach
void setUp() {
applicationContext.start();
}

@Test
@Timeout(value = 10)
public void shouldCustomizeNamespace() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,11 @@
import io.temporal.common.converter.DefaultDataConverter;
import io.temporal.spring.boot.autoconfigure.bytaskqueue.TestWorkflow;
import io.temporal.testing.TestWorkflowEnvironment;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
Expand All @@ -45,19 +43,12 @@
@ActiveProfiles(profiles = "auto-discovery-by-task-queue")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class CustomDataConverterTest {
@Autowired ConfigurableApplicationContext applicationContext;

@Autowired TestWorkflowEnvironment testWorkflowEnvironment;

@Autowired WorkflowClient workflowClient;

@Autowired DataConverter spyDataConverter;

@BeforeEach
void setUp() {
applicationContext.start();
}

@Test
@Timeout(value = 10)
public void testCustomDataConverterBeanIsPickedUpByTestWorkflowEnvironment() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@
import io.temporal.client.WorkflowOptions;
import io.temporal.spring.boot.autoconfigure.bytaskqueue.TestWorkflow;
import io.temporal.testing.TestWorkflowEnvironment;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.test.context.ActiveProfiles;
Expand All @@ -39,17 +37,10 @@
@ActiveProfiles(profiles = "explicit-config")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class ExplicitConfigTest {
@Autowired ConfigurableApplicationContext applicationContext;

@Autowired TestWorkflowEnvironment testWorkflowEnvironment;

@Autowired WorkflowClient workflowClient;

@BeforeEach
void setUp() {
applicationContext.start();
}

@Test
@Timeout(value = 10)
public void testExplicitConfig() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,11 @@
import io.temporal.testing.TestEnvironmentOptions;
import io.temporal.worker.WorkerFactoryOptions;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
Expand All @@ -46,16 +44,9 @@
@ActiveProfiles(profiles = "auto-discovery-by-task-queue")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class OptionsCustomizersTest {
@Autowired ConfigurableApplicationContext applicationContext;

@Autowired List<TemporalOptionsCustomizer<?>> customizers;
@Autowired WorkerOptionsCustomizer workerCustomizer;

@BeforeEach
void setUp() {
applicationContext.start();
}

@Test
@Timeout(value = 10)
public void testCustomizersGotCalled() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,30 +29,63 @@
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.test.context.ActiveProfiles;

@SpringBootTest(classes = AutoDiscoveryByTaskQueueTest.Configuration.class)
@SpringBootTest(
classes = {
AutoDiscoveryByTaskQueueTest.Configuration.class,
WorkersAreNotStartingBeforeContextTest.EventChecks.class,
})
@ActiveProfiles(profiles = "auto-discovery-by-task-queue")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class WorkersAreNotStartingBeforeContextTest {
@Autowired ConfigurableApplicationContext applicationContext;

@Autowired TestWorkflowEnvironment testWorkflowEnvironment;

@Autowired WorkerFactory workerFactory;
@Autowired EventChecks eventChecks;

@Test
@Timeout(value = 10)
public void testWorkersAreGettingStartedOnlyWhenStartOfSpringContextIsCalled() {
assertFalse(
workerFactory.isStarted(),
eventChecks.startedAtCtxRefresh,
"Context refresh or initialization shouldn't cause workers start");
assertFalse(
eventChecks.startedAtAppStarted, "Application Started shouldn't cause workers start");
applicationContext.start();
assertTrue(workerFactory.isStarted());
assertTrue(eventChecks.startedAtAppReady, "Workers should be started at Application Ready");
}

public static class EventChecks {
boolean startedAtAppStarted;
boolean startedAtAppReady;
boolean startedAtCtxRefresh;

@Autowired WorkerFactory workerFactory;

@EventListener
public void applicationStarted(ContextRefreshedEvent event) {
startedAtAppStarted = workerFactory.isStarted();
}

@EventListener
public void applicationStarted(ApplicationStartedEvent event) {
startedAtAppStarted = workerFactory.isStarted();
}

@EventListener
public void applicationStarted(ApplicationReadyEvent event) {
startedAtAppReady = workerFactory.isStarted();
}
}

@ComponentScan(
Expand Down

0 comments on commit 2540b7b

Please sign in to comment.