Commit 34d4f127 by kevin peters Committed by Johannes Edmeier

Prevent SBA Client's TaskScheduler to be picked up by @Scheduled

The TaskScheduler used for periodically registering is removed from the application context and moved to the ApplicationRegistrationListener. see #478
parent 9a80b625
...@@ -29,7 +29,6 @@ import java.util.Collections; ...@@ -29,7 +29,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration; import org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
...@@ -46,8 +45,6 @@ import org.springframework.context.annotation.Configuration; ...@@ -46,8 +45,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
...@@ -107,21 +104,10 @@ public class SpringBootAdminClientAutoConfiguration { ...@@ -107,21 +104,10 @@ public class SpringBootAdminClientAutoConfiguration {
} }
@Bean @Bean
@Qualifier("registrationTaskScheduler")
public TaskScheduler registrationTaskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(1);
taskScheduler.setRemoveOnCancelPolicy(true);
taskScheduler.setThreadNamePrefix("registrationTask");
return taskScheduler;
}
@Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public RegistrationApplicationListener registrationListener(ClientProperties client, public RegistrationApplicationListener registrationListener(ClientProperties client,
ApplicationRegistrator registrator) { ApplicationRegistrator registrator) {
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator);
registrationTaskScheduler());
listener.setAutoRegister(client.isAutoRegistration()); listener.setAutoRegister(client.isAutoRegistration());
listener.setAutoDeregister(client.isAutoDeregistration()); listener.setAutoDeregister(client.isAutoDeregistration());
listener.setRegisterPeriod(client.getPeriod()); listener.setRegisterPeriod(client.getPeriod());
......
...@@ -20,12 +20,14 @@ import java.time.Duration; ...@@ -20,12 +20,14 @@ import java.time.Duration;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
/** /**
* Listener responsible for starting and stopping the registration task when the application is * Listener responsible for starting and stopping the registration task when the application is
...@@ -33,16 +35,28 @@ import org.springframework.scheduling.TaskScheduler; ...@@ -33,16 +35,28 @@ import org.springframework.scheduling.TaskScheduler;
* *
* @author Johannes Edmeier * @author Johannes Edmeier
*/ */
public class RegistrationApplicationListener { public class RegistrationApplicationListener implements InitializingBean, DisposableBean {
private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationApplicationListener.class); private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationApplicationListener.class);
private final ApplicationRegistrator registrator; private final ApplicationRegistrator registrator;
private final TaskScheduler taskScheduler; private final ThreadPoolTaskScheduler taskScheduler;
private boolean autoDeregister = false; private boolean autoDeregister = false;
private boolean autoRegister = true; private boolean autoRegister = true;
private Duration registerPeriod = Duration.ofSeconds(10); private Duration registerPeriod = Duration.ofSeconds(10);
private volatile ScheduledFuture<?> scheduledTask; private volatile ScheduledFuture<?> scheduledTask;
public RegistrationApplicationListener(ApplicationRegistrator registrator, TaskScheduler taskScheduler) { public RegistrationApplicationListener(ApplicationRegistrator registrator) {
this(registrator, registrationTaskScheduler());
}
private static ThreadPoolTaskScheduler registrationTaskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(1);
taskScheduler.setRemoveOnCancelPolicy(true);
taskScheduler.setThreadNamePrefix("registrationTask");
return taskScheduler;
}
RegistrationApplicationListener(ApplicationRegistrator registrator, ThreadPoolTaskScheduler taskScheduler) {
this.registrator = registrator; this.registrator = registrator;
this.taskScheduler = taskScheduler; this.taskScheduler = taskScheduler;
} }
...@@ -94,4 +108,14 @@ public class RegistrationApplicationListener { ...@@ -94,4 +108,14 @@ public class RegistrationApplicationListener {
public void setRegisterPeriod(Duration registerPeriod) { public void setRegisterPeriod(Duration registerPeriod) {
this.registerPeriod = registerPeriod; this.registerPeriod = registerPeriod;
} }
@Override
public void afterPropertiesSet() {
taskScheduler.afterPropertiesSet();
}
@Override
public void destroy() {
taskScheduler.destroy();
}
} }
...@@ -22,7 +22,7 @@ import org.junit.Test; ...@@ -22,7 +22,7 @@ import org.junit.Test;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextClosedEvent;
import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.context.ConfigurableWebApplicationContext; import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
...@@ -30,15 +30,16 @@ import static org.mockito.ArgumentMatchers.eq; ...@@ -30,15 +30,16 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA; import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
public class RegistrationApplicationListenerTest { public class RegistrationApplicationListenerTest {
@Test @Test
public void test_register() { public void should_schedule_register_task() {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class); ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
TaskScheduler scheduler = mock(TaskScheduler.class); ThreadPoolTaskScheduler scheduler = mock(ThreadPoolTaskScheduler.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, scheduler); RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, scheduler);
listener.onApplicationReady(new ApplicationReadyEvent(mock(SpringApplication.class), null, listener.onApplicationReady(new ApplicationReadyEvent(mock(SpringApplication.class), null,
...@@ -48,9 +49,9 @@ public class RegistrationApplicationListenerTest { ...@@ -48,9 +49,9 @@ public class RegistrationApplicationListenerTest {
} }
@Test @Test
public void test_no_register() { public void should_no_schedule_register_task_when_not_autoRegister() {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class); ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
TaskScheduler scheduler = mock(TaskScheduler.class); ThreadPoolTaskScheduler scheduler = mock(ThreadPoolTaskScheduler.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, scheduler); RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, scheduler);
listener.setAutoRegister(false); listener.setAutoRegister(false);
...@@ -60,34 +61,31 @@ public class RegistrationApplicationListenerTest { ...@@ -60,34 +61,31 @@ public class RegistrationApplicationListenerTest {
verify(scheduler, never()).scheduleAtFixedRate(isA(Runnable.class), eq(Duration.ofSeconds(10))); verify(scheduler, never()).scheduleAtFixedRate(isA(Runnable.class), eq(Duration.ofSeconds(10)));
} }
@SuppressWarnings({"unchecked", "rawtypes"})
@Test @Test
public void test_no_register_after_close() { public void should_cancel_register_task_on_context_close() {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class); ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
TaskScheduler scheduler = mock(TaskScheduler.class); ThreadPoolTaskScheduler scheduler = mock(ThreadPoolTaskScheduler.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, scheduler); RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, scheduler);
ScheduledFuture task = mock(ScheduledFuture.class); ScheduledFuture<?> task = mock(ScheduledFuture.class);
when(scheduler.scheduleAtFixedRate(isA(Runnable.class), eq(Duration.ofSeconds(10)))).thenReturn(task); when(scheduler.scheduleAtFixedRate(isA(Runnable.class), eq(Duration.ofSeconds(10)))).then(invocation -> task);
listener.onApplicationReady(new ApplicationReadyEvent(mock(SpringApplication.class), null, listener.onApplicationReady(new ApplicationReadyEvent(mock(SpringApplication.class), null,
mock(ConfigurableWebApplicationContext.class))); mock(ConfigurableWebApplicationContext.class)));
verify(scheduler).scheduleAtFixedRate(isA(Runnable.class), eq(Duration.ofSeconds(10))); verify(scheduler).scheduleAtFixedRate(isA(Runnable.class), eq(Duration.ofSeconds(10)));
listener.onClosedContext(new ContextClosedEvent(mock(WebApplicationContext.class))); listener.onClosedContext(new ContextClosedEvent(mock(WebApplicationContext.class)));
verify(task).cancel(true); verify(task).cancel(true);
} }
@SuppressWarnings({"unchecked", "rawtypes"})
@Test @Test
public void test_start_stop() { public void should_start_and_cancel_task_on_request() {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class); ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
TaskScheduler scheduler = mock(TaskScheduler.class); ThreadPoolTaskScheduler scheduler = mock(ThreadPoolTaskScheduler.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, scheduler); RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, scheduler);
ScheduledFuture task = mock(ScheduledFuture.class); ScheduledFuture<?> task = mock(ScheduledFuture.class);
when(scheduler.scheduleAtFixedRate(isA(Runnable.class), eq(Duration.ofSeconds(10)))).thenReturn(task); when(scheduler.scheduleAtFixedRate(isA(Runnable.class), eq(Duration.ofSeconds(10)))).then(invocation -> task);
listener.startRegisterTask(); listener.startRegisterTask();
verify(scheduler).scheduleAtFixedRate(isA(Runnable.class), eq(Duration.ofSeconds(10))); verify(scheduler).scheduleAtFixedRate(isA(Runnable.class), eq(Duration.ofSeconds(10)));
...@@ -97,9 +95,9 @@ public class RegistrationApplicationListenerTest { ...@@ -97,9 +95,9 @@ public class RegistrationApplicationListenerTest {
} }
@Test @Test
public void test_no_deregister() { public void should_not_deregister_when_not_autoDeregister() {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class); ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
TaskScheduler scheduler = mock(TaskScheduler.class); ThreadPoolTaskScheduler scheduler = mock(ThreadPoolTaskScheduler.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, scheduler); RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, scheduler);
listener.onClosedContext(new ContextClosedEvent(mock(WebApplicationContext.class))); listener.onClosedContext(new ContextClosedEvent(mock(WebApplicationContext.class)));
...@@ -108,9 +106,9 @@ public class RegistrationApplicationListenerTest { ...@@ -108,9 +106,9 @@ public class RegistrationApplicationListenerTest {
} }
@Test @Test
public void test_deregister() { public void should_deregister_when_autoDeregister() {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class); ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
TaskScheduler scheduler = mock(TaskScheduler.class); ThreadPoolTaskScheduler scheduler = mock(ThreadPoolTaskScheduler.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, scheduler); RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, scheduler);
listener.setAutoDeregister(true); listener.setAutoDeregister(true);
...@@ -118,4 +116,17 @@ public class RegistrationApplicationListenerTest { ...@@ -118,4 +116,17 @@ public class RegistrationApplicationListenerTest {
verify(registrator).deregister(); verify(registrator).deregister();
} }
@Test
public void should_init_and_shutdown_taskScheduler() {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
ThreadPoolTaskScheduler scheduler = mock(ThreadPoolTaskScheduler.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator, scheduler);
listener.afterPropertiesSet();
verify(scheduler, times(1)).afterPropertiesSet();
listener.destroy();
verify(scheduler, times(1)).destroy();
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment