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
This commit is contained in:
Stephane Nicoll
2015-06-04 17:35:09 +02:00
parent cb98ee25ff
commit c177a774a5
7 changed files with 44 additions and 11 deletions

View File

@@ -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;

View File

@@ -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]]

View File

@@ -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<String, Object> map;
if (!sources.contains("server.ports")) {
map = new HashMap<String, Object>();
MapPropertySource source = new MapPropertySource("server.ports", map);
sources.addFirst(source);
}
else {
@SuppressWarnings("unchecked")
Map<String, Object> value = (Map<String, Object>) sources.get("server.ports")
.getSource();
map = value;
}
map.put(propertyName, port);
}
if (context.getParent() != null) {
setPortProperty(context.getParent(), propertyName, port);

View File

@@ -198,7 +198,6 @@ public class SpringApplicationContextLoader extends AbstractContextLoader {
List<ApplicationContextInitializer<?>> initializers = new ArrayList<ApplicationContextInitializer<?>>();
initializers.add(new PropertySourceLocationsInitializer(mergedConfig
.getPropertySourceLocations()));
initializers.add(new ServerPortInfoApplicationContextInitializer());
initializers.addAll(application.getInitializers());
for (Class<? extends ApplicationContextInitializer<?>> initializerClass : mergedConfig
.getContextInitializerClasses()) {

View File

@@ -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=\

View File

@@ -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

View File

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