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 c81058a0..136a575b 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 @@ -21,21 +21,26 @@ import javax.annotation.PreDestroy; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties; +import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; 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.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 { + ApplicationContextAware, ApplicationListener { private boolean autoStartup = true; - private boolean running; + private AtomicBoolean running = new AtomicBoolean(false); private int order = 0; @@ -43,6 +48,8 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle, private Environment environment; + private AtomicInteger port = new AtomicInteger(0); + protected ApplicationContext getContext() { return context; } @@ -58,6 +65,10 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle, return environment; } + protected AtomicInteger getPort() { + return port; + } + @Override public boolean isAutoStartup() { return this.autoStartup; @@ -75,15 +86,26 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle, return; } - register(); - if (shouldRegisterManagement()) { - registerManagement(); + // only set the port if the nonSecurePort is 0 and this.port != 0 + if (this.port.get() != 0 && getConfiguredPort() == 0) { + setConfiguredPort(this.port.get()); + } + // only initialize if nonSecurePort is greater than 0 and it isn't already running + // because of containerPortInitializer below + if (!this.running.get() && getConfiguredPort() > 0) { + register(); + if (shouldRegisterManagement()) { + registerManagement(); + } + this.context .publishEvent(new InstanceRegisteredEvent<>(this, + getConfiguration())); + this.running.compareAndSet(false, true); } - this.context - .publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration())); - this.running = true; } + protected abstract int getConfiguredPort(); + protected abstract void setConfiguredPort(int port); + /** * @return if the management service should be registered with the DiscoveryService */ @@ -171,7 +193,7 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle, deregisterManagement(); } } - this.running = false; + this.running.compareAndSet(true, false); } @PreDestroy @@ -181,7 +203,7 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle, @Override public boolean isRunning() { - return this.running; + return this.running.get(); } @Override @@ -194,4 +216,10 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle, return 0; } + @Override + public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) { + // TODO: take SSL into account when Spring Boot 1.2 is available + this.port.compareAndSet(0, event.getEmbeddedServletContainer().getPort()); + this.start(); + } } 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 new file mode 100644 index 00000000..b206a107 --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/discovery/AbstractDiscoveryLifecycleTests.java @@ -0,0 +1,85 @@ +package org.springframework.cloud.client.discovery; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +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.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Spencer Gibb + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = AbstractDiscoveryLifecycleTests.Config.class) +@WebIntegrationTest(randomPort = true) +public class AbstractDiscoveryLifecycleTests { + + @Autowired + private TestDiscoveryLifecycle lifecycle; + + @Test + public void randomPortWorks() { + assertNotEquals("Lifecycle port is zero", 0, lifecycle.getPort().get()); + assertTrue("Lifecycle not running", lifecycle.isRunning()); + assertTrue("Lifecycle not registered", lifecycle.isRegistered()); + } + + @EnableAutoConfiguration + @Configuration + public static class Config { + @Bean + public TestDiscoveryLifecycle testDiscoveryLifecycle() { + return new TestDiscoveryLifecycle(); + } + } + + public static class TestDiscoveryLifecycle extends AbstractDiscoveryLifecycle { + private int port = 0; + private boolean registered = false; + private boolean deregistered = false; + + @Override + protected int getConfiguredPort() { + return port; + } + + @Override + protected void setConfiguredPort(int port) { + this.port = port; + } + + @Override + protected Object getConfiguration() { + return this; + } + + @Override + protected void register() { + this.registered = true; + } + + @Override + protected void deregister() { + this.deregistered = true; + } + + @Override + protected boolean isEnabled() { + return true; + } + + public boolean isRegistered() { + return registered; + } + + public boolean isDeregistered() { + return deregistered; + } + } +}