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();