From c177a774a5f5b2531154dd4e0b46c5c00ea79b0a Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 4 Jun 2015 17:35:09 +0200 Subject: [PATCH] Provide seamless support for local.server.port Previously, the actual HTTP port on which a web application is running on was only exposed in tests. This commit makes sure to provide that feature regardless of the environment so that applications can know on which port they are actually running on. If there are several containers, each is exposed via the namespace of their respective application context. Closes gh-3259 --- .../EndpointWebMvcAutoConfigurationTests.java | 2 +- spring-boot-docs/src/main/asciidoc/howto.adoc | 3 +++ ...PortInfoApplicationContextInitializer.java | 27 ++++++++++++++++--- .../test/SpringApplicationContextLoader.java | 1 - .../main/resources/META-INF/spring.factories | 3 ++- .../SpringApplicationBuilderTests.java | 8 +++--- .../EmbeddedWebApplicationContextTests.java | 11 ++++++++ 7 files changed, 44 insertions(+), 11 deletions(-) rename spring-boot/src/main/java/org/springframework/boot/{test => context/web}/ServerPortInfoApplicationContextInitializer.java (78%) diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java index c6ad430f12..de1aea38e9 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java @@ -50,8 +50,8 @@ import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebAppl import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; +import org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer; import org.springframework.boot.test.EnvironmentTestUtils; -import org.springframework.boot.test.ServerPortInfoApplicationContextInitializer; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; diff --git a/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-docs/src/main/asciidoc/howto.adoc index 27650ea782..6540890be2 100644 --- a/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -400,6 +400,9 @@ code. === Use a random unassigned HTTP port To scan for a free port (using OS natives to prevent clashes) use `server.port=0`. +TIP: You can know what port got allocated at runtime by looking at the `local.server.port` +property in the `Environment`. + [[howto-discover-the-http-port-at-runtime]] diff --git a/spring-boot/src/main/java/org/springframework/boot/test/ServerPortInfoApplicationContextInitializer.java b/spring-boot/src/main/java/org/springframework/boot/context/web/ServerPortInfoApplicationContextInitializer.java similarity index 78% rename from spring-boot/src/main/java/org/springframework/boot/test/ServerPortInfoApplicationContextInitializer.java rename to spring-boot/src/main/java/org/springframework/boot/context/web/ServerPortInfoApplicationContextInitializer.java index 4359ad43ab..180c6c9b84 100644 --- a/spring-boot/src/main/java/org/springframework/boot/test/ServerPortInfoApplicationContextInitializer.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/web/ServerPortInfoApplicationContextInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -14,7 +14,10 @@ * limitations under the License. */ -package org.springframework.boot.test; +package org.springframework.boot.context.web; + +import java.util.HashMap; +import java.util.Map; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.embedded.EmbeddedServletContainer; @@ -24,7 +27,10 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; import org.springframework.util.StringUtils; /** @@ -75,8 +81,21 @@ public class ServerPortInfoApplicationContextInitializer implements private void setPortProperty(ApplicationContext context, String propertyName, int port) { if (context instanceof ConfigurableApplicationContext) { - EnvironmentTestUtils.addEnvironment((ConfigurableApplicationContext) context, - propertyName + ":" + port); + ConfigurableEnvironment environment = ((ConfigurableApplicationContext) context).getEnvironment(); + MutablePropertySources sources = environment.getPropertySources(); + Map map; + if (!sources.contains("server.ports")) { + map = new HashMap(); + MapPropertySource source = new MapPropertySource("server.ports", map); + sources.addFirst(source); + } + else { + @SuppressWarnings("unchecked") + Map value = (Map) sources.get("server.ports") + .getSource(); + map = value; + } + map.put(propertyName, port); } if (context.getParent() != null) { setPortProperty(context.getParent(), propertyName, port); diff --git a/spring-boot/src/main/java/org/springframework/boot/test/SpringApplicationContextLoader.java b/spring-boot/src/main/java/org/springframework/boot/test/SpringApplicationContextLoader.java index 30b71aed96..d0e3f9e608 100644 --- a/spring-boot/src/main/java/org/springframework/boot/test/SpringApplicationContextLoader.java +++ b/spring-boot/src/main/java/org/springframework/boot/test/SpringApplicationContextLoader.java @@ -198,7 +198,6 @@ public class SpringApplicationContextLoader extends AbstractContextLoader { List> initializers = new ArrayList>(); initializers.add(new PropertySourceLocationsInitializer(mergedConfig .getPropertySourceLocations())); - initializers.add(new ServerPortInfoApplicationContextInitializer()); initializers.addAll(application.getInitializers()); for (Class> initializerClass : mergedConfig .getContextInitializerClasses()) { diff --git a/spring-boot/src/main/resources/META-INF/spring.factories b/spring-boot/src/main/resources/META-INF/spring.factories index a68f02672d..ca654d5930 100644 --- a/spring-boot/src/main/resources/META-INF/spring.factories +++ b/spring-boot/src/main/resources/META-INF/spring.factories @@ -11,7 +11,8 @@ org.springframework.boot.context.event.EventPublishingRunListener org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ -org.springframework.boot.context.config.DelegatingApplicationContextInitializer +org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ +org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer # Application Listeners org.springframework.context.ApplicationListener=\ diff --git a/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java b/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java index 02200827c0..e0429330e2 100644 --- a/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -239,7 +239,7 @@ public class SpringApplicationBuilderTests { SpringApplicationBuilder application = new SpringApplicationBuilder( ExampleConfig.class).web(false); this.context = application.run(); - assertEquals(3, application.application().getInitializers().size()); + assertEquals(4, application.application().getInitializers().size()); } @Test @@ -247,7 +247,7 @@ public class SpringApplicationBuilderTests { SpringApplicationBuilder application = new SpringApplicationBuilder( ExampleConfig.class).child(ChildConfig.class).web(false); this.context = application.run(); - assertEquals(4, application.application().getInitializers().size()); + assertEquals(5, application.application().getInitializers().size()); } @Test @@ -261,7 +261,7 @@ public class SpringApplicationBuilderTests { } }); this.context = application.run(); - assertEquals(4, application.application().getInitializers().size()); + assertEquals(5, application.application().getInitializers().size()); } @Configuration diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContextTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContextTests.java index 68b7e4c39e..b48f567ceb 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContextTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContextTests.java @@ -41,6 +41,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.Scope; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer; import org.springframework.context.ApplicationContextException; import org.springframework.context.ApplicationListener; import org.springframework.context.support.AbstractApplicationContext; @@ -73,6 +74,7 @@ import static org.mockito.Mockito.withSettings; * Tests for {@link EmbeddedWebApplicationContext}. * * @author Phillip Webb + * @author Stephane Nicoll */ public class EmbeddedWebApplicationContextTests { @@ -144,6 +146,15 @@ public class EmbeddedWebApplicationContextTests { assertEquals(this.context, event.getApplicationContext()); } + @Test + public void localPortIsAvailable() throws Exception { + addEmbeddedServletContainerFactoryBean(); + new ServerPortInfoApplicationContextInitializer().initialize(this.context); + this.context.refresh(); + assertTrue(this.context.getEnvironment().containsProperty("local.server.port")); + assertEquals("8080", this.context.getEnvironment().getProperty("local.server.port")); + } + @Test public void stopOnClose() throws Exception { addEmbeddedServletContainerFactoryBean();