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; ...@@ -23,12 +23,19 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties; import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener; 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; import org.springframework.util.StringUtils;
@ConfigurationProperties(prefix = "spring.boot.admin.client", ignoreUnknownFields = false) @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; private String url;
...@@ -45,17 +52,40 @@ public class AdminClientProperties implements ApplicationListener<EmbeddedServle ...@@ -45,17 +52,40 @@ public class AdminClientProperties implements ApplicationListener<EmbeddedServle
private int managementPort = -1; private int managementPort = -1;
private boolean serverInitialized = false;
@Override @Override
public void onApplicationEvent( public void onApplicationEvent(ApplicationEvent event) {
EmbeddedServletContainerInitializedEvent event) { if (event instanceof EmbeddedServletContainerInitializedEvent) {
if ("management".equals(event.getApplicationContext().getNamespace())) { EmbeddedServletContainerInitializedEvent initEvent = (EmbeddedServletContainerInitializedEvent) event;
managementPort = event.getEmbeddedServletContainer().getPort(); serverInitialized = true;
if ("management".equals(initEvent.getApplicationContext().getNamespace())) {
managementPort = initEvent.getEmbeddedServletContainer().getPort();
} else { } else {
serverPort = event.getEmbeddedServletContainer().getPort(); 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 * @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. * reachable URL is different (e.g. Docker). Must be unique in registry.
...@@ -63,12 +93,12 @@ public class AdminClientProperties implements ApplicationListener<EmbeddedServle ...@@ -63,12 +93,12 @@ public class AdminClientProperties implements ApplicationListener<EmbeddedServle
public String getUrl() { public String getUrl() {
if (url == null) { if (url == null) {
if (managementPort != -1) { if (managementPort != -1) {
url = "http://" return "http://"
+ (getHostname() + ':' + managementPort + toPathFragment(management + (getHostname() + ':' + managementPort + toPathFragment(management
.getContextPath())) .getContextPath()))
.replaceAll("//+", "/"); .replaceAll("//+", "/");
} else if (serverPort != -1){ } else if (serverPort != -1){
url = "http://" return "http://"
+ (getHostname() + (getHostname()
+ ':' + ':'
+ serverPort + serverPort
...@@ -87,6 +117,10 @@ public class AdminClientProperties implements ApplicationListener<EmbeddedServle ...@@ -87,6 +117,10 @@ public class AdminClientProperties implements ApplicationListener<EmbeddedServle
this.url = url; this.url = url;
} }
public boolean isServerInitialized() {
return serverInitialized;
}
/** /**
* @return Name to register with. * @return Name to register with.
*/ */
......
...@@ -21,15 +21,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; ...@@ -21,15 +21,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; 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.Bean;
import org.springframework.context.annotation.Configuration; 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.client.ClientHttpRequestInterceptor;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.scheduling.config.ScheduledTaskRegistrar;
...@@ -37,6 +30,7 @@ import org.springframework.web.client.RestTemplate; ...@@ -37,6 +30,7 @@ import org.springframework.web.client.RestTemplate;
import de.codecentric.boot.admin.actuate.LogfileMvcEndpoint; import de.codecentric.boot.admin.actuate.LogfileMvcEndpoint;
import de.codecentric.boot.admin.services.ApplicationRegistrator; import de.codecentric.boot.admin.services.ApplicationRegistrator;
import de.codecentric.boot.admin.services.RegistrationApplicationListener;
import de.codecentric.boot.admin.web.BasicAuthHttpRequestInterceptor; import de.codecentric.boot.admin.web.BasicAuthHttpRequestInterceptor;
/** /**
...@@ -75,14 +69,13 @@ public class SpringBootAdminClientAutoConfiguration { ...@@ -75,14 +69,13 @@ public class SpringBootAdminClientAutoConfiguration {
*/ */
@Bean @Bean
public ScheduledTaskRegistrar taskRegistrar(final ApplicationRegistrator registrator, public ScheduledTaskRegistrar taskRegistrar(final ApplicationRegistrator registrator,
final ConfigurableApplicationContext context, AdminProperties admin, final AdminClientProperties client) {
AdminProperties admin) {
ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar(); ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();
Runnable registratorTask = new Runnable() { Runnable registratorTask = new Runnable() {
@Override @Override
public void run() { public void run() {
if (context.isActive()) { if (client.isServerInitialized()) {
registrator.register(); registrator.register();
} }
} }
...@@ -93,25 +86,13 @@ public class SpringBootAdminClientAutoConfiguration { ...@@ -93,25 +86,13 @@ public class SpringBootAdminClientAutoConfiguration {
} }
/** /**
* ApplicationListener triggering rigestration after refresh/shutdown * ApplicationListener triggering registration after refresh/shutdown
*/ */
@Bean @Bean
public ApplicationListener<ApplicationContextEvent> RegistrationListener( public RegistrationApplicationListener registrationListener(
final ApplicationRegistrator registrator, final AdminProperties admin) { final ApplicationRegistrator registrator, final AdminProperties admin) {
return new ApplicationListener<ApplicationContextEvent>() { return new RegistrationApplicationListener(admin, registrator);
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();
}
} }
};
}
@Configuration @Configuration
@ConditionalOnExpression("${endpoints.logfile.enabled:true}") @ConditionalOnExpression("${endpoints.logfile.enabled:true}")
......
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; package de.codecentric.boot.admin.config;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
...@@ -16,6 +18,7 @@ import org.springframework.boot.context.embedded.EmbeddedServletContainer; ...@@ -16,6 +18,7 @@ import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext; import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
public class AdminClientPropertiesTest { public class AdminClientPropertiesTest {
...@@ -30,14 +33,40 @@ public class AdminClientPropertiesTest { ...@@ -30,14 +33,40 @@ public class AdminClientPropertiesTest {
} }
@Test @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() { public void test_mgmtPortPath() {
load("spring.boot.admin.url:http://localhost:8081", load("management.contextPath=/admin");
"management.contextPath=/admin"); AdminClientProperties clientProperties = new AdminClientProperties();
AdminClientProperties clientProperties = context context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
.getBean(AdminClientProperties.class);
publishEvent(8080, null); publishServletContainerInitializedEvent(clientProperties, 8080, null);
publishEvent(8081, "management"); publishServletContainerInitializedEvent(clientProperties, 8081, "management");
assertThat(clientProperties.getUrl(), is("http://" + getHostname() assertThat(clientProperties.getUrl(), is("http://" + getHostname()
+ ":8081/admin")); + ":8081/admin"));
...@@ -45,24 +74,24 @@ public class AdminClientPropertiesTest { ...@@ -45,24 +74,24 @@ public class AdminClientPropertiesTest {
@Test @Test
public void test_mgmtPort() { public void test_mgmtPort() {
load("spring.boot.admin.url:http://localhost:8081"); load();
AdminClientProperties clientProperties = context AdminClientProperties clientProperties = new AdminClientProperties();
.getBean(AdminClientProperties.class); context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
publishEvent(8080, null); publishServletContainerInitializedEvent(clientProperties, 8080, null);
publishEvent(8081, "management"); publishServletContainerInitializedEvent(clientProperties, 8081, "management");
assertThat(clientProperties.getUrl(), is("http://" + getHostname() + ":8081")); assertThat(clientProperties.getUrl(), is("http://" + getHostname() + ":8081"));
} }
@Test @Test
public void test_contextPath_mgmtPath() { 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"); "management.context-path=/admin");
AdminClientProperties clientProperties = context AdminClientProperties clientProperties = new AdminClientProperties();
.getBean(AdminClientProperties.class); context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
publishEvent(8080, null); publishServletContainerInitializedEvent(clientProperties, 8080, null);
assertThat(clientProperties.getUrl(), is("http://" + getHostname() assertThat(clientProperties.getUrl(), is("http://" + getHostname()
+ ":8080/app/admin")); + ":8080/app/admin"));
...@@ -71,11 +100,11 @@ public class AdminClientPropertiesTest { ...@@ -71,11 +100,11 @@ public class AdminClientPropertiesTest {
@Test @Test
public void test_contextPath() { public void test_contextPath() {
load("spring.boot.admin.url:http://localhost:8081", "server.context-path=app"); load("server.context-path=app");
AdminClientProperties clientProperties = context AdminClientProperties clientProperties = new AdminClientProperties();
.getBean(AdminClientProperties.class); context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
publishEvent(8080, null); publishServletContainerInitializedEvent(clientProperties, 8080, null);
assertThat(clientProperties.getUrl(), is("http://" + getHostname() + ":8080/app")); assertThat(clientProperties.getUrl(), is("http://" + getHostname() + ":8080/app"));
} }
...@@ -83,11 +112,11 @@ public class AdminClientPropertiesTest { ...@@ -83,11 +112,11 @@ public class AdminClientPropertiesTest {
@Test @Test
public void test_default() { public void test_default() {
load("spring.boot.admin.url:http://localhost:8081"); load();
AdminClientProperties clientProperties = context AdminClientProperties clientProperties = new AdminClientProperties();
.getBean(AdminClientProperties.class); context.getAutowireCapableBeanFactory().autowireBean(clientProperties);
publishEvent(8080, null); publishServletContainerInitializedEvent(clientProperties, 8080, null);
assertThat(clientProperties.getUrl(), is("http://" + getHostname() + ":8080")); assertThat(clientProperties.getUrl(), is("http://" + getHostname() + ":8080"));
} }
...@@ -101,20 +130,27 @@ public class AdminClientPropertiesTest { ...@@ -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); EmbeddedServletContainer eventSource = mock(EmbeddedServletContainer.class);
when(eventSource.getPort()).thenReturn(port); when(eventSource.getPort()).thenReturn(port);
EmbeddedWebApplicationContext eventContext = mock(EmbeddedWebApplicationContext.class); EmbeddedWebApplicationContext eventContext = mock(EmbeddedWebApplicationContext.class);
when(eventContext.getNamespace()).thenReturn(namespace); when(eventContext.getNamespace()).thenReturn(namespace);
context.publishEvent(new EmbeddedServletContainerInitializedEvent(eventContext, when(eventContext.getEmbeddedServletContainer()).thenReturn(eventSource);
client.onApplicationEvent(new EmbeddedServletContainerInitializedEvent(
eventContext,
eventSource)); eventSource));
} }
private void publishContextRefreshedEvent(AdminClientProperties client) {
client.onApplicationEvent(new ContextRefreshedEvent(
mock(EmbeddedWebApplicationContext.class)));
}
private void load(String... environment) { private void load(String... environment) {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext(); AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(ServerPropertiesAutoConfiguration.class); applicationContext.register(ServerPropertiesAutoConfiguration.class);
applicationContext.register(ManagementServerPropertiesAutoConfiguration.class); applicationContext.register(ManagementServerPropertiesAutoConfiguration.class);
applicationContext.register(SpringBootAdminClientAutoConfiguration.class);
EnvironmentTestUtils.addEnvironment(applicationContext, environment); EnvironmentTestUtils.addEnvironment(applicationContext, environment);
applicationContext.refresh(); applicationContext.refresh();
this.context = applicationContext; 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