Remove getEmbeddedServletContainers()

Remove the mutable getEmbeddedServletContainers() Map from
EmbeddedWebApplicationContext and instead use the `namespace` to
distinguish the management container.

The ServerPortInfoApplicationContextInitializer class replaces the
previous TestExecutionListener to exposes port properties (by
listening for EmbeddedServletContainerInitializedEvents).
This commit is contained in:
Phillip Webb 2014-04-23 15:18:29 +01:00
parent fad5ce45db
commit f847ed2b1f
7 changed files with 93 additions and 91 deletions

View File

@ -53,9 +53,7 @@ import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoCo
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
@ -173,6 +171,7 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext();
childContext.setParent(this.applicationContext);
childContext.setNamespace("management");
childContext.setId(this.applicationContext.getId() + ":management");
// Register the ManagementServerChildContextConfiguration first followed
@ -197,8 +196,6 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
}
try {
childContext.refresh();
registerContainer(this.applicationContext,
childContext.getEmbeddedServletContainer());
}
catch (RuntimeException ex) {
// No support currently for deploying a war with management.port=<different>,
@ -213,16 +210,6 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
}
};
private void registerContainer(ApplicationContext applicationContext,
EmbeddedServletContainer embeddedServletContainer) {
if (applicationContext instanceof EmbeddedWebApplicationContext) {
((EmbeddedWebApplicationContext) applicationContext)
.getEmbeddedServletContainers().put("management",
embeddedServletContainer);
// Maybe unregister it when it shuts down?
}
}
protected static enum ManagementServerPort {
DISABLE, SAME, DIFFERENT;

View File

@ -16,7 +16,6 @@
package org.springframework.boot.context.embedded;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
/**
@ -29,10 +28,11 @@ import org.springframework.context.ApplicationEvent;
*/
public class EmbeddedServletContainerInitializedEvent extends ApplicationEvent {
private final ApplicationContext applicationContext;
private final EmbeddedWebApplicationContext applicationContext;
public EmbeddedServletContainerInitializedEvent(
ApplicationContext applicationContext, EmbeddedServletContainer source) {
EmbeddedWebApplicationContext applicationContext,
EmbeddedServletContainer source) {
super(source);
this.applicationContext = applicationContext;
}
@ -60,7 +60,7 @@ public class EmbeddedServletContainerInitializedEvent extends ApplicationEvent {
* context) before acting on the server container itself.
* @return the applicationContext that the container was created from
*/
public ApplicationContext getApplicationContext() {
public EmbeddedWebApplicationContext getApplicationContext() {
return this.applicationContext;
}

View File

@ -21,7 +21,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EventListener;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@ -89,8 +88,6 @@ import org.springframework.web.context.support.WebApplicationContextUtils;
*/
public class EmbeddedWebApplicationContext extends GenericWebApplicationContext {
private static final String DEFAULT_SERVER_NAME = "server";
/**
* Constant value for the DispatcherServlet bean name. A Servlet bean with this name
* is deemed to be the "main" servlet and is automatically given a mapping of "/" by
@ -105,8 +102,6 @@ public class EmbeddedWebApplicationContext extends GenericWebApplicationContext
private String namespace;
private Map<String, EmbeddedServletContainer> containers = new HashMap<String, EmbeddedServletContainer>();
/**
* Register ServletContextAwareProcessor.
* @see ServletContextAwareProcessor
@ -163,7 +158,6 @@ public class EmbeddedWebApplicationContext extends GenericWebApplicationContext
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
this.embeddedServletContainer = containerFactory
.getEmbeddedServletContainer(getSelfInitializer());
this.containers.put(DEFAULT_SERVER_NAME, this.embeddedServletContainer);
}
else if (getServletContext() != null) {
try {
@ -388,7 +382,6 @@ public class EmbeddedWebApplicationContext extends GenericWebApplicationContext
try {
this.embeddedServletContainer.stop();
this.embeddedServletContainer = null;
this.containers.remove(DEFAULT_SERVER_NAME);
}
catch (Exception ex) {
throw new IllegalStateException(ex);
@ -432,14 +425,4 @@ public class EmbeddedWebApplicationContext extends GenericWebApplicationContext
return this.embeddedServletContainer;
}
/**
* A registry of embedded containers by name. The
* {@link #getEmbeddedServletContainer() canonical container} is called "server".
* Anyone else who creates one can register it with whatever name they please.
* @return the containers
*/
public Map<String, EmbeddedServletContainer> getEmbeddedServletContainers() {
return this.containers;
}
}

View File

@ -1,53 +0,0 @@
/*
* Copyright 2012-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test;
import java.util.Map;
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
/**
* Listener that injects the server port into an {@link Environment} property named
* {@literal local.&lt;server&gt;.port}. Useful when the server is running on a dynamic
* port.
*
* @author Dave Syer
*/
public class EmbeddedServletContainerTestExecutionListener extends
AbstractTestExecutionListener {
@Override
public void prepareTestInstance(TestContext testContext) throws Exception {
ApplicationContext context = testContext.getApplicationContext();
if (context instanceof EmbeddedWebApplicationContext) {
prepareTestInstance((EmbeddedWebApplicationContext) context);
}
}
private void prepareTestInstance(EmbeddedWebApplicationContext context) {
for (Map.Entry<String, EmbeddedServletContainer> entry : context
.getEmbeddedServletContainers().entrySet()) {
EnvironmentTestUtils.addEnvironment(context, "local." + entry.getKey()
+ ".port:" + entry.getValue().getPort());
}
}
}

View File

@ -41,9 +41,7 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
@Target(ElementType.TYPE)
// Leave out the ServletTestExecutionListener because it only deals with Mock* servlet
// stuff. A real embedded application will not need the mocks.
@TestExecutionListeners(listeners = {
EmbeddedServletContainerTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
@TestExecutionListeners(listeners = { DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class })
public @interface IntegrationTest {

View File

@ -0,0 +1,86 @@
/*
* Copyright 2012-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
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.Environment;
import org.springframework.util.StringUtils;
/**
* {@link ApplicationContextInitializer} that sets {@link Environment} properties for the
* ports that {@link EmbeddedServletContainer} servers are actually listening on. The
* property {@literal "local.server.port"} can be injected directly into tests using
* {@link Value @Value} or obtained via the {@link Environment}.
* <p>
* If the {@link EmbeddedWebApplicationContext} has a
* {@link EmbeddedWebApplicationContext#setNamespace(String) namespace} set, it will be
* used to construct the property name. For example, the "management" actuator context
* will have the property name {@literal "local.management.port"}.
* <p>
* Properties are automatically propagated up to any parent context.
*
* @author Dave Syer
* @author Phillip Webb
*/
public class ServerPortInfoApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext
.addApplicationListener(new ApplicationListener<EmbeddedServletContainerInitializedEvent>() {
@Override
public void onApplicationEvent(
EmbeddedServletContainerInitializedEvent event) {
ServerPortInfoApplicationContextInitializer.this
.onApplicationEvent(event);
}
});
}
protected void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
String propertyName = getPropertyName(event.getApplicationContext());
setPortProperty(event.getApplicationContext(), propertyName, event
.getEmbeddedServletContainer().getPort());
}
protected String getPropertyName(EmbeddedWebApplicationContext context) {
String name = context.getNamespace();
if (StringUtils.isEmpty(name)) {
name = "server";
}
return "local." + name + ".port";
}
private void setPortProperty(ApplicationContext context, String propertyName, int port) {
if (context instanceof ConfigurableApplicationContext) {
EnvironmentTestUtils.addEnvironment((ConfigurableApplicationContext) context,
propertyName + ":" + port);
}
if (context.getParent() != null) {
setPortProperty(context.getParent(), propertyName, port);
}
}
}

View File

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