Commit b7577fdd by Johannes Stelzer

fixup! Add optional automatic deregistration on shutdown - turned off by…

fixup! Add optional automatic deregistration on shutdown - turned off by default; only periodically try to register when the context is active
parent d66e70f3
......@@ -23,12 +23,19 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
@ConfigurationProperties(prefix = "spring.boot.admin.client", ignoreUnknownFields = false)
public class AdminClientProperties implements ApplicationListener<EmbeddedServletContainerInitializedEvent>{
@Order(Ordered.LOWEST_PRECEDENCE - 100)
public class AdminClientProperties implements ApplicationListener<ApplicationEvent> {
private String url;
......@@ -45,17 +52,40 @@ public class AdminClientProperties implements ApplicationListener<EmbeddedServle
private int managementPort = -1;
private boolean serverInitialized = false;
@Override
public void onApplicationEvent(
EmbeddedServletContainerInitializedEvent event) {
if ("management".equals(event.getApplicationContext().getNamespace())) {
managementPort = event.getEmbeddedServletContainer().getPort();
} else {
serverPort = event.getEmbeddedServletContainer().getPort();
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof EmbeddedServletContainerInitializedEvent) {
EmbeddedServletContainerInitializedEvent initEvent = (EmbeddedServletContainerInitializedEvent) event;
serverInitialized = true;
if ("management".equals(initEvent.getApplicationContext().getNamespace())) {
managementPort = initEvent.getEmbeddedServletContainer().getPort();
} else {
serverPort = initEvent.getEmbeddedServletContainer().getPort();
}
} else if (startedDeployedWar(event)) {
serverInitialized = true;
if (!StringUtils.hasText(url)) {
throw new RuntimeException(
"spring.boot.admin.client.url must be set for deployed war files!");
}
}
}
private boolean startedDeployedWar(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
ApplicationContextEvent contextEvent = (ApplicationContextEvent) event;
if (contextEvent.getApplicationContext() instanceof EmbeddedWebApplicationContext) {
EmbeddedWebApplicationContext context = (EmbeddedWebApplicationContext) contextEvent
.getApplicationContext();
return context.getEmbeddedServletContainer() == null;
}
}
return false;
}
/**
* @return Client-management-URL to register with. Can be overriden in case the
* reachable URL is different (e.g. Docker). Must be unique in registry.
......@@ -63,12 +93,12 @@ public class AdminClientProperties implements ApplicationListener<EmbeddedServle
public String getUrl() {
if (url == null) {
if (managementPort != -1) {
url = "http://"
return "http://"
+ (getHostname() + ':' + managementPort + toPathFragment(management
.getContextPath()))
.replaceAll("//+", "/");
} else if (serverPort != -1){
url = "http://"
return "http://"
+ (getHostname()
+ ':'
+ serverPort
......@@ -87,6 +117,10 @@ public class AdminClientProperties implements ApplicationListener<EmbeddedServle
this.url = url;
}
public boolean isServerInitialized() {
return serverInitialized;
}
/**
* @return Name to register with.
*/
......
......@@ -21,15 +21,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
......@@ -37,6 +30,7 @@ import org.springframework.web.client.RestTemplate;
import de.codecentric.boot.admin.actuate.LogfileMvcEndpoint;
import de.codecentric.boot.admin.services.ApplicationRegistrator;
import de.codecentric.boot.admin.services.RegistrationApplicationListener;
import de.codecentric.boot.admin.web.BasicAuthHttpRequestInterceptor;
/**
......@@ -75,14 +69,13 @@ public class SpringBootAdminClientAutoConfiguration {
*/
@Bean
public ScheduledTaskRegistrar taskRegistrar(final ApplicationRegistrator registrator,
final ConfigurableApplicationContext context,
AdminProperties admin) {
AdminProperties admin, final AdminClientProperties client) {
ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();
Runnable registratorTask = new Runnable() {
@Override
public void run() {
if (context.isActive()) {
if (client.isServerInitialized()) {
registrator.register();
}
}
......@@ -93,26 +86,14 @@ public class SpringBootAdminClientAutoConfiguration {
}
/**
* ApplicationListener triggering rigestration after refresh/shutdown
* ApplicationListener triggering registration after refresh/shutdown
*/
@Bean
public ApplicationListener<ApplicationContextEvent> RegistrationListener(
public RegistrationApplicationListener registrationListener(
final ApplicationRegistrator registrator, final AdminProperties admin) {
return new ApplicationListener<ApplicationContextEvent>() {
public void onApplicationEvent(ApplicationContextEvent event) {
if (event instanceof ContextRefreshedEvent
|| event instanceof ContextStartedEvent) {
registrator.register();
}
else if (admin.isAutoDeregistration()
&& (event instanceof ContextClosedEvent || event instanceof ContextStoppedEvent)) {
registrator.deregister();
}
}
};
return new RegistrationApplicationListener(admin, registrator);
}
@Configuration
@ConditionalOnExpression("${endpoints.logfile.enabled:true}")
@ConditionalOnProperty("logging.file")
......
package de.codecentric.boot.admin.services;
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import de.codecentric.boot.admin.config.AdminProperties;
@Order(Ordered.LOWEST_PRECEDENCE)
public class RegistrationApplicationListener implements
ApplicationListener<ApplicationEvent> {
private final AdminProperties admin;
private final ApplicationRegistrator registrator;
public RegistrationApplicationListener(AdminProperties admin,
ApplicationRegistrator registrator) {
this.admin = admin;
this.registrator = registrator;
}
public void onApplicationEvent(ApplicationEvent event) {
if (startedDeployedWar(event) || startedEmbeddedServer(event)) {
registrator.register();
}
else if (admin.isAutoDeregistration()
&& event instanceof ContextClosedEvent) {
registrator.deregister();
}
}
private boolean startedEmbeddedServer(ApplicationEvent event) {
return event instanceof EmbeddedServletContainerInitializedEvent;
}
private boolean startedDeployedWar(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
ApplicationContextEvent contextEvent = (ApplicationContextEvent) event;
if (contextEvent.getApplicationContext() instanceof EmbeddedWebApplicationContext) {
EmbeddedWebApplicationContext context = (EmbeddedWebApplicationContext) contextEvent
.getApplicationContext();
return context.getEmbeddedServletContainer() == null;
}
}
return false;
}
}
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;
import static org.mockito.Mockito.when;
......@@ -16,6 +18,7 @@ import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
public class AdminClientPropertiesTest {
......@@ -30,14 +33,40 @@ public class AdminClientPropertiesTest {
}
@Test
public void test_isServerStarted_false() {
assertFalse(new AdminClientProperties().isServerInitialized());
}
@Test
public void test_isServerStarted_true_embedded() {
AdminClientProperties clientProperties = new AdminClientProperties();
clientProperties.setUrl("http://localhost");
publishServletContainerInitializedEvent(clientProperties, 8080, null);
assertTrue(clientProperties.isServerInitialized());
}
@Test
public void test_isServerStarted_true_war() {
AdminClientProperties clientProperties = new AdminClientProperties();
clientProperties.setUrl("http://localhost");
publishContextRefreshedEvent(clientProperties);
assertTrue(clientProperties.isServerInitialized());
}
@Test(expected = RuntimeException.class)
public void test_isServerStarted_exception_war() {
AdminClientProperties clientProperties = new AdminClientProperties();
publishContextRefreshedEvent(clientProperties);
}
@Test
public void test_mgmtPortPath() {
load("spring.boot.admin.url:http://localhost:8081",
"management.contextPath=/admin");
AdminClientProperties clientProperties = context
.getBean(AdminClientProperties.class);
load("management.contextPath=/admin");
AdminClientProperties clientProperties = new AdminClientProperties();
context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
publishEvent(8080, null);
publishEvent(8081, "management");
publishServletContainerInitializedEvent(clientProperties, 8080, null);
publishServletContainerInitializedEvent(clientProperties, 8081, "management");
assertThat(clientProperties.getUrl(), is("http://" + getHostname()
+ ":8081/admin"));
......@@ -45,24 +74,24 @@ public class AdminClientPropertiesTest {
@Test
public void test_mgmtPort() {
load("spring.boot.admin.url:http://localhost:8081");
AdminClientProperties clientProperties = context
.getBean(AdminClientProperties.class);
load();
AdminClientProperties clientProperties = new AdminClientProperties();
context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
publishEvent(8080, null);
publishEvent(8081, "management");
publishServletContainerInitializedEvent(clientProperties, 8080, null);
publishServletContainerInitializedEvent(clientProperties, 8081, "management");
assertThat(clientProperties.getUrl(), is("http://" + getHostname() + ":8081"));
}
@Test
public void test_contextPath_mgmtPath() {
load("spring.boot.admin.url:http://localhost:8081", "server.context-path=app",
load("server.context-path=app",
"management.context-path=/admin");
AdminClientProperties clientProperties = context
.getBean(AdminClientProperties.class);
AdminClientProperties clientProperties = new AdminClientProperties();
context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
publishEvent(8080, null);
publishServletContainerInitializedEvent(clientProperties, 8080, null);
assertThat(clientProperties.getUrl(), is("http://" + getHostname()
+ ":8080/app/admin"));
......@@ -71,11 +100,11 @@ public class AdminClientPropertiesTest {
@Test
public void test_contextPath() {
load("spring.boot.admin.url:http://localhost:8081", "server.context-path=app");
AdminClientProperties clientProperties = context
.getBean(AdminClientProperties.class);
load("server.context-path=app");
AdminClientProperties clientProperties = new AdminClientProperties();
context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
publishEvent(8080, null);
publishServletContainerInitializedEvent(clientProperties, 8080, null);
assertThat(clientProperties.getUrl(), is("http://" + getHostname() + ":8080/app"));
}
......@@ -83,11 +112,11 @@ public class AdminClientPropertiesTest {
@Test
public void test_default() {
load("spring.boot.admin.url:http://localhost:8081");
AdminClientProperties clientProperties = context
.getBean(AdminClientProperties.class);
load();
AdminClientProperties clientProperties = new AdminClientProperties();
context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
publishEvent(8080, null);
publishServletContainerInitializedEvent(clientProperties, 8080, null);
assertThat(clientProperties.getUrl(), is("http://" + getHostname() + ":8080"));
}
......@@ -101,20 +130,27 @@ public class AdminClientPropertiesTest {
}
}
private void publishEvent(int port, String namespace) {
private void publishServletContainerInitializedEvent(AdminClientProperties client,
int port, String namespace) {
EmbeddedServletContainer eventSource = mock(EmbeddedServletContainer.class);
when(eventSource.getPort()).thenReturn(port);
EmbeddedWebApplicationContext eventContext = mock(EmbeddedWebApplicationContext.class);
when(eventContext.getNamespace()).thenReturn(namespace);
context.publishEvent(new EmbeddedServletContainerInitializedEvent(eventContext,
when(eventContext.getEmbeddedServletContainer()).thenReturn(eventSource);
client.onApplicationEvent(new EmbeddedServletContainerInitializedEvent(
eventContext,
eventSource));
}
private void publishContextRefreshedEvent(AdminClientProperties client) {
client.onApplicationEvent(new ContextRefreshedEvent(
mock(EmbeddedWebApplicationContext.class)));
}
private void load(String... environment) {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(ServerPropertiesAutoConfiguration.class);
applicationContext.register(ManagementServerPropertiesAutoConfiguration.class);
applicationContext.register(SpringBootAdminClientAutoConfiguration.class);
EnvironmentTestUtils.addEnvironment(applicationContext, environment);
applicationContext.refresh();
this.context = applicationContext;
......
package de.codecentric.boot.admin.services;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import de.codecentric.boot.admin.config.AdminProperties;
public class RegistrationApplicationListenerTest {
private AdminProperties admin;
@Before
public void setup() {
admin = new AdminProperties();
}
@Test
public void test_register_embedded() {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(
admin, registrator);
listener.onApplicationEvent(new EmbeddedServletContainerInitializedEvent(
mock(EmbeddedWebApplicationContext.class),
mock(EmbeddedServletContainer.class)));
verify(registrator).register();
}
@Test
public void test_register_war() {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(
admin, registrator);
listener.onApplicationEvent(new ContextRefreshedEvent(
mock(EmbeddedWebApplicationContext.class)));
verify(registrator).register();
}
@Test
public void test_no_register_war() {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(
admin, registrator);
EmbeddedWebApplicationContext context = mock(EmbeddedWebApplicationContext.class);
when(context.getEmbeddedServletContainer()).thenReturn(
mock(EmbeddedServletContainer.class));
listener.onApplicationEvent(new ContextRefreshedEvent(context));
verify(registrator, never()).register();
}
@Test
public void test_no_deregister() {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(
admin, registrator);
listener.onApplicationEvent(new ContextClosedEvent(
mock(EmbeddedWebApplicationContext.class)));
verify(registrator, never()).deregister();
}
@Test
public void test_deregister() {
ApplicationRegistrator registrator = mock(ApplicationRegistrator.class);
RegistrationApplicationListener listener = new RegistrationApplicationListener(
admin, registrator);
admin.setAutoDeregistration(true);
listener.onApplicationEvent(new ContextClosedEvent(
mock(EmbeddedWebApplicationContext.class)));
verify(registrator).deregister();
}
}
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