From f76687e949a8fb612d037b2d0d9800d9094cb2f8 Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Mon, 25 Apr 2016 16:33:28 -0600 Subject: [PATCH] Start discovery lifecycle in traditional war deployments. fixes https://github.com/spring-cloud/spring-cloud-consul/issues/173 --- .../discovery/AbstractDiscoveryLifecycle.java | 46 +++++++++++++++--- .../AbstractDiscoveryLifecycleTests.java | 28 +++++++++++ .../AbstractDiscoveryLifecycleWarTests.java | 47 +++++++++++++++++++ 3 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 spring-cloud-commons/src/test/java/org/springframework/cloud/client/discovery/AbstractDiscoveryLifecycleWarTests.java diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/AbstractDiscoveryLifecycle.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/AbstractDiscoveryLifecycle.java index c9ef3243..49d3dc40 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/AbstractDiscoveryLifecycle.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/AbstractDiscoveryLifecycle.java @@ -16,25 +16,32 @@ package org.springframework.cloud.client.discovery; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + import javax.annotation.PreDestroy; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; +import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext; import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; import org.springframework.core.env.Environment; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - /** * Lifecycle methods that may be useful and common to various DiscoveryClient implementations. * @author Spencer Gibb */ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle, - ApplicationContextAware, ApplicationListener { + ApplicationContextAware { + + private final Log logger = LogFactory.getLog(getClass()); private boolean autoStartup = true; @@ -48,6 +55,9 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle, private AtomicInteger port = new AtomicInteger(0); + @Value("${server.port:${SERVER_PORT:${PORT:-1}}}") + private Integer serverPort; + protected ApplicationContext getContext() { return context; } @@ -203,8 +213,8 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle, return 0; } - @Override - public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) { + @EventListener + public void handleContainerInit(EmbeddedServletContainerInitializedEvent event) { // TODO: take SSL into account // Don't register the management port as THE port if (!"management".equals(event.getApplicationContext().getNamespace())) { @@ -212,4 +222,26 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle, this.start(); } } + + /** + * Handles the start() method for a traditional war deployment. + * @param event + */ + @EventListener + public void handleContextRefreshed(ContextRefreshedEvent event) { + if (!isEmbeddedContext(event)) { + if (this.serverPort == null || this.serverPort <= 0) { + throw new IllegalStateException("Running as a traditional war and server.port is not set"); + } else { + this.logger.info("Starting AbstractDiscoveryLifecycle during traditional war deployment using server.port: " + this.serverPort); + this.port.compareAndSet(0, this.serverPort); + this.start(); + } + } + } + + protected boolean isEmbeddedContext(ContextRefreshedEvent event) { + return event.getApplicationContext() instanceof EmbeddedWebApplicationContext + && ((EmbeddedWebApplicationContext) event.getApplicationContext()).getEmbeddedServletContainer() != null; + } } diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/discovery/AbstractDiscoveryLifecycleTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/discovery/AbstractDiscoveryLifecycleTests.java index 306d9e21..2a40ff32 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/discovery/AbstractDiscoveryLifecycleTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/discovery/AbstractDiscoveryLifecycleTests.java @@ -7,10 +7,12 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.WebIntegrationTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** @@ -38,6 +40,8 @@ public class AbstractDiscoveryLifecycleTests { assertTrue("Lifecycle not running", lifecycle.isRunning()); assertTrue("Lifecycle not registered", lifecycle.isRegistered()); assertEquals("Lifecycle appName is wrong", "application", lifecycle.getAppName()); + assertTrue("Lifecycle didn't handle containerInit", lifecycle.isContainerInitHandled()); + assertFalse("Lifecycle handled contextRefreshed", lifecycle.isContextRefreshedHandled()); } @EnableAutoConfiguration @@ -53,6 +57,8 @@ public class AbstractDiscoveryLifecycleTests { private int port = 0; private boolean registered = false; private boolean deregistered = false; + private boolean containerInitHandled = false; + private boolean contextRefreshedHandled = false; @Override protected int getConfiguredPort() { @@ -84,6 +90,20 @@ public class AbstractDiscoveryLifecycleTests { return true; } + @Override + public void handleContainerInit(EmbeddedServletContainerInitializedEvent event) { + this.containerInitHandled = true; + super.handleContainerInit(event); + } + + @Override + public void handleContextRefreshed(ContextRefreshedEvent event) { + if (!super.isEmbeddedContext(event)) { + this.contextRefreshedHandled = true; + } + super.handleContextRefreshed(event); + } + public boolean isRegistered() { return registered; } @@ -91,5 +111,13 @@ public class AbstractDiscoveryLifecycleTests { public boolean isDeregistered() { return deregistered; } + + public boolean isContainerInitHandled() { + return containerInitHandled; + } + + public boolean isContextRefreshedHandled() { + return contextRefreshedHandled; + } } } diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/discovery/AbstractDiscoveryLifecycleWarTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/discovery/AbstractDiscoveryLifecycleWarTests.java new file mode 100644 index 00000000..a710ebf5 --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/discovery/AbstractDiscoveryLifecycleWarTests.java @@ -0,0 +1,47 @@ +package org.springframework.cloud.client.discovery; + +import org.junit.Test; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.test.ImportAutoConfiguration; +import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.cloud.client.discovery.AbstractDiscoveryLifecycleTests.TestDiscoveryLifecycle; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Spencer Gibb + */ +public class AbstractDiscoveryLifecycleWarTests { + + @Test + public void portsWork() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(Config.class); + EnvironmentTestUtils.addEnvironment(context.getEnvironment(), "server.port=7777"); + context.refresh(); + + TestDiscoveryLifecycle lifecycle = context.getBean(TestDiscoveryLifecycle.class); + assertEquals("Lifecycle port is wrong", 7777, lifecycle.getPort().get()); + assertFalse("Lifecycle handled containerInit", lifecycle.isContainerInitHandled()); + assertTrue("Lifecycle didn't handle contextRefreshed", lifecycle.isContextRefreshedHandled()); + } + + @Test(expected = IllegalStateException.class) + public void portsFailIfNotSet() { + new AnnotationConfigApplicationContext(Config.class); + } + + @ImportAutoConfiguration({PropertyPlaceholderAutoConfiguration.class}) + @Configuration + public static class Config { + @Bean + public TestDiscoveryLifecycle testDiscoveryLifecycle() { + return new TestDiscoveryLifecycle(); + } + } +}