Commit 84cfa693 by Johannes Edmeier

Add spring.boot.admin.auto-registration property

This commit contains multiple changes: - Start the registration task after the ApplicationReady event. This makes AdminClientProperties.isReady() unnecessary. - The RegistarApplicationListener has two new public methods to start/stop the periodic registration task. - Add spring.boot.admin.auto-registration property (default: true). When this property is set the registration task is automatically scheduled after the ApplicationReadyEvent. closes #151
parent 762e24ef
......@@ -207,6 +207,10 @@ spring.boot.admin.password
| Interval for repeating the registration (in ms).
| `10.000`
| spring.boot.admin.auto-registration
| If set to true the periodic task to register the application is automatically scheduled after the application is ready.
| `true`
| spring.boot.admin.auto-deregistration
| Switch to enable auto-deregistration at Spring Boot Admin server when context is closed.
| `false`
......@@ -432,6 +436,7 @@ To get reminders for down/offline applications you can add a `RemindingNotifier`
[source,java]
----
@Configuration
@EnableScheduling
public class ReminderConfiguration {
@Autowired
private Notifier notifier;
......
......@@ -71,15 +71,12 @@ public class AdminClientProperties {
private Integer managementPort;
private boolean ready = false;
@EventListener
public void onApplicationReady(ApplicationReadyEvent event) {
serverPort = event.getApplicationContext().getEnvironment().getProperty("local.server.port",
Integer.class);
managementPort = event.getApplicationContext().getEnvironment()
.getProperty("local.management.port", Integer.class, serverPort);
ready = true;
}
public String getManagementUrl() {
......@@ -93,7 +90,7 @@ public class AdminClientProperties {
management.getContextPath());
}
if (ready && managementPort == null) {
if (managementPort == null) {
throw new IllegalStateException(
"serviceUrl must be set when deployed to servlet-container");
}
......@@ -133,7 +130,7 @@ public class AdminClientProperties {
return serviceUrl;
}
if (ready && serverPort == null) {
if (serverPort == null) {
throw new IllegalStateException(
"serviceUrl must be set when deployed to servlet-container");
}
......@@ -155,10 +152,6 @@ public class AdminClientProperties {
this.serviceUrl = serviceUrl;
}
public boolean isReady() {
return ready;
}
public String getName() {
return name;
}
......
......@@ -33,7 +33,7 @@ public class AdminProperties {
/**
* Time interval (in ms) the registration is repeated
*/
private int period = 10000;
private long period = 10_000L;
/**
* Username for basic authentication on admin server
......@@ -50,6 +50,11 @@ public class AdminProperties {
*/
private boolean autoDeregistration;
/**
* Enable automatic registration when the application is ready
*/
private boolean autoRegistration = true;
public void setUrl(String[] url) {
this.url = url.clone();
}
......@@ -74,7 +79,7 @@ public class AdminProperties {
return adminUrls;
}
public int getPeriod() {
public long getPeriod() {
return period;
}
......@@ -105,4 +110,12 @@ public class AdminProperties {
public void setAutoDeregistration(boolean autoDeregistration) {
this.autoDeregistration = autoDeregistration;
}
public boolean isAutoRegistration() {
return autoRegistration;
}
public void setAutoRegistration(boolean autoRegistration) {
this.autoRegistration = autoRegistration;
}
}
......@@ -25,18 +25,12 @@ import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.web.client.RestTemplate;
import de.codecentric.boot.admin.services.ApplicationRegistrator;
import de.codecentric.boot.admin.services.RegistrationApplicationListener;
import de.codecentric.boot.admin.web.BasicAuthHttpRequestInterceptor;
/**
* This configuration adds a registrator bean to the spring context. This bean checks periodicaly,
* if the using application is registered at the spring-boot-admin application. If not, it registers
* itself.
*/
@Configuration
@EnableConfigurationProperties({ AdminProperties.class, AdminClientProperties.class })
@Conditional(SpringBootAdminClientEnabledCondition.class)
......@@ -70,33 +64,16 @@ public class SpringBootAdminClientAutoConfiguration {
}
/**
* TaskRegistrar that triggers the RegistratorTask every ten seconds.
*/
@Bean
public ScheduledTaskRegistrar taskRegistrar() {
ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();
Runnable registratorTask = new Runnable() {
@Override
public void run() {
if (client.isReady()) {
registrator().register();
}
}
};
registrar.addFixedRateTask(registratorTask, admin.getPeriod());
return registrar;
}
/**
* ApplicationListener triggering registration after refresh/shutdown
* ApplicationListener triggering registration after being ready/shutdown
*/
@Bean
@ConditionalOnMissingBean
public RegistrationApplicationListener registrationListener() {
RegistrationApplicationListener listener = new RegistrationApplicationListener(
registrator());
listener.setAutoRegister(admin.isAutoRegistration());
listener.setAutoDeregister(admin.isAutoDeregistration());
listener.setRegisterPeriod(admin.getPeriod());
return listener;
}
}
/*
* Copyright 2014 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
*
* http://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 de.codecentric.boot.admin.services;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
/**
* Listener responsible for starting and stopping the registration task when the application is
* ready.
*
* @author Johannes Edmeier
*/
public class RegistrationApplicationListener {
private static Logger LOGGER = LoggerFactory.getLogger(RegistrationApplicationListener.class);
private final ApplicationRegistrator registrator;
private final TaskScheduler taskScheduler;
private boolean autoDeregister = false;
private final TaskExecutor executor;
private boolean autoRegister = true;
private long registerPeriod = 10_000L;
private volatile ScheduledFuture<?> scheduledTask;
public RegistrationApplicationListener(ApplicationRegistrator registrator,
TaskExecutor executor) {
TaskScheduler taskScheduler) {
this.registrator = registrator;
this.executor = executor;
this.taskScheduler = taskScheduler;
}
public RegistrationApplicationListener(ApplicationRegistrator registrator,
ScheduledExecutorService scheduler) {
this(registrator, new ConcurrentTaskScheduler(scheduler));
}
public RegistrationApplicationListener(ApplicationRegistrator registrator) {
this(registrator, new SimpleAsyncTaskExecutor());
this(registrator, Executors.newSingleThreadScheduledExecutor());
}
@EventListener
@Order(Ordered.LOWEST_PRECEDENCE)
public void onApplicationReady(ApplicationReadyEvent event) {
executor.execute(new Runnable() {
@Override
public void run() {
registrator.register();
}
});
if (autoRegister) {
startRegisterTask();
}
}
@EventListener
@Order(Ordered.LOWEST_PRECEDENCE)
public void onClosedContext(ContextClosedEvent event) {
stopRegisterTask();
if (autoDeregister) {
executor.execute(new Runnable() {
@Override
public void run() {
registrator.deregister();
}
});
registrator.deregister();
}
}
public void startRegisterTask() {
if (scheduledTask != null && !scheduledTask.isDone()) {
return;
}
scheduledTask = taskScheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
registrator.register();
}
}, registerPeriod);
LOGGER.debug("Scheduled registration task for every {}ms", registerPeriod);
}
public void stopRegisterTask() {
if (scheduledTask != null && !scheduledTask.isDone()) {
scheduledTask.cancel(true);
LOGGER.debug("Canceled registration task");
}
}
......@@ -51,7 +103,11 @@ public class RegistrationApplicationListener {
this.autoDeregister = autoDeregister;
}
public boolean isAutoDeregister() {
return autoDeregister;
public void setAutoRegister(boolean autoRegister) {
this.autoRegister = autoRegister;
}
public void setRegisterPeriod(long registerPeriod) {
this.registerPeriod = registerPeriod;
}
}
package de.codecentric.boot.admin.config;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
......@@ -31,21 +30,6 @@ public class AdminClientPropertiesTest {
}
@Test
public void test_isServerStarted_false() {
assertFalse(new AdminClientProperties().isReady());
}
@Test
public void test_isServerStarted_true() {
load();
AdminClientProperties clientProperties = new AdminClientProperties();
publishApplicationReadyEvent(clientProperties);
assertTrue(clientProperties.isReady());
}
@Test
public void test_mgmtPortPath() {
load("management.contextPath=/admin", "endpoints.health.id=alive", "local.server.port=8080",
"local.management.port=8081");
......
......@@ -9,39 +9,38 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.core.task.SyncTaskExecutor;
public class RegistrationApplicationListenerTest {
@Test
public void test_register_embedded() {
public void test_register() throws Exception {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator,
new SyncTaskExecutor());
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator);
listener.onApplicationReady(
new ApplicationReadyEvent(mock(SpringApplication.class), null, null));
Thread.sleep(500);
verify(registrator).register();
}
@Test
public void test_register_war() {
public void test_no_register() throws Exception {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator,
new SyncTaskExecutor());
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator);
listener.setAutoRegister(false);
listener.onApplicationReady(
new ApplicationReadyEvent(mock(SpringApplication.class), null, null));
verify(registrator).register();
Thread.sleep(500);
verify(registrator, never()).register();
}
@Test
public void test_no_deregister() {
public void test_no_deregister() throws Exception {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator,
new SyncTaskExecutor());
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator);
listener.onClosedContext(new ContextClosedEvent(mock(EmbeddedWebApplicationContext.class)));
......@@ -49,10 +48,9 @@ public class RegistrationApplicationListenerTest {
}
@Test
public void test_deregister() {
public void test_deregister() throws Exception {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator,
new SyncTaskExecutor());
RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator);
listener.setAutoDeregister(true);
listener.onClosedContext(new ContextClosedEvent(mock(EmbeddedWebApplicationContext.class)));
......
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