From f847ed2b1f861b1ff22c4f637c8ac34c3e6fa338 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 23 Apr 2014 15:18:29 +0100 Subject: [PATCH] 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). --- .../EndpointWebMvcAutoConfiguration.java | 15 +--- ...eddedServletContainerInitializedEvent.java | 8 +- .../EmbeddedWebApplicationContext.java | 17 ---- ...ServletContainerTestExecutionListener.java | 53 ------------ .../boot/test/IntegrationTest.java | 4 +- ...PortInfoApplicationContextInitializer.java | 86 +++++++++++++++++++ .../test/SpringApplicationContextLoader.java | 1 + 7 files changed, 93 insertions(+), 91 deletions(-) delete mode 100644 spring-boot/src/main/java/org/springframework/boot/test/EmbeddedServletContainerTestExecutionListener.java create mode 100644 spring-boot/src/main/java/org/springframework/boot/test/ServerPortInfoApplicationContextInitializer.java diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java index 90c7bf7f69..0ad878b4bd 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java @@ -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=, @@ -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; diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/EmbeddedServletContainerInitializedEvent.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/EmbeddedServletContainerInitializedEvent.java index 2dafedbf09..0ef192594c 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/EmbeddedServletContainerInitializedEvent.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/EmbeddedServletContainerInitializedEvent.java @@ -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; } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContext.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContext.java index 71cf966338..dfde2065df 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContext.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContext.java @@ -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 containers = new HashMap(); - /** * 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 getEmbeddedServletContainers() { - return this.containers; - } - } diff --git a/spring-boot/src/main/java/org/springframework/boot/test/EmbeddedServletContainerTestExecutionListener.java b/spring-boot/src/main/java/org/springframework/boot/test/EmbeddedServletContainerTestExecutionListener.java deleted file mode 100644 index e2a1d136e3..0000000000 --- a/spring-boot/src/main/java/org/springframework/boot/test/EmbeddedServletContainerTestExecutionListener.java +++ /dev/null @@ -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.<server>.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 entry : context - .getEmbeddedServletContainers().entrySet()) { - EnvironmentTestUtils.addEnvironment(context, "local." + entry.getKey() - + ".port:" + entry.getValue().getPort()); - } - } -} diff --git a/spring-boot/src/main/java/org/springframework/boot/test/IntegrationTest.java b/spring-boot/src/main/java/org/springframework/boot/test/IntegrationTest.java index 177b718040..f8c19e8225 100644 --- a/spring-boot/src/main/java/org/springframework/boot/test/IntegrationTest.java +++ b/spring-boot/src/main/java/org/springframework/boot/test/IntegrationTest.java @@ -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 { diff --git a/spring-boot/src/main/java/org/springframework/boot/test/ServerPortInfoApplicationContextInitializer.java b/spring-boot/src/main/java/org/springframework/boot/test/ServerPortInfoApplicationContextInitializer.java new file mode 100644 index 0000000000..f8b3320fd6 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/test/ServerPortInfoApplicationContextInitializer.java @@ -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}. + *

+ * 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"}. + *

+ * Properties are automatically propagated up to any parent context. + * + * @author Dave Syer + * @author Phillip Webb + */ +public class ServerPortInfoApplicationContextInitializer implements + ApplicationContextInitializer { + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + applicationContext + .addApplicationListener(new ApplicationListener() { + @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); + } + } + +} 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 f1f3c50fae..5fa353b83e 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 @@ -174,6 +174,7 @@ public class SpringApplicationContextLoader extends AbstractContextLoader { private List> getInitializers( MergedContextConfiguration mergedConfig, SpringApplication application) { List> initializers = new ArrayList>(); + initializers.add(new ServerPortInfoApplicationContextInitializer()); initializers.addAll(application.getInitializers()); for (Class> initializerClass : mergedConfig .getContextInitializerClasses()) {