diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.java index 0bc891edf75..c55e0ff640f 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.java @@ -159,9 +159,11 @@ public class EndpointWebMvcManagementContextConfiguration { @Bean @ConditionalOnBean(HealthEndpoint.class) @ConditionalOnEnabledEndpoint("health") - public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate) { + public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate, + ManagementServerProperties managementServerProperties) { HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate, - this.managementServerProperties.getSecurity().isEnabled()); + this.managementServerProperties.getSecurity().isEnabled(), + managementServerProperties.getSecurity().getRoles()); if (this.healthMvcEndpointProperties.getMapping() != null) { healthMvcEndpoint .addStatusMapping(this.healthMvcEndpointProperties.getMapping()); diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpoint.java index ada10fec517..053a5832300 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpoint.java @@ -16,7 +16,9 @@ package org.springframework.boot.actuate.endpoint.mvc; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; @@ -52,6 +54,8 @@ public class HealthMvcEndpoint extends AbstractEndpointMvcAdapter roles; + private Map statusMapping = new HashMap(); private RelaxedPropertyResolver securityPropertyResolver; @@ -65,9 +69,15 @@ public class HealthMvcEndpoint extends AbstractEndpointMvcAdapter roles) { super(delegate); this.secure = secure; setupDefaultStatusMapping(); + this.roles = roles; } private void setupDefaultStatusMapping() { @@ -173,10 +183,7 @@ public class HealthMvcEndpoint extends AbstractEndpointMvcAdapter getRoles() { + if (this.roles != null) { + return this.roles; + } + String[] roles = StringUtils.commaDelimitedListToStringArray( + this.securityPropertyResolver.getProperty("roles", "ROLE_ACTUATOR")); + roles = StringUtils.trimArrayElements(roles); + return Arrays.asList(roles); + } + } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptor.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptor.java index d60b271912a..4607ef2e4f6 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptor.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptor.java @@ -125,10 +125,14 @@ public class MvcEndpointSecurityInterceptor extends HandlerInterceptorAdapter { } } - private class AuthoritiesValidator { + /** + * Inner class to check authorities using Spring Security (when available). + */ + private static class AuthoritiesValidator { private boolean hasAuthority(String role) { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + Authentication authentication = SecurityContextHolder.getContext() + .getAuthentication(); if (authentication != null) { for (GrantedAuthority authority : authentication.getAuthorities()) { if (authority.getAuthority().equals(role)) { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthMvcEndpointAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthMvcEndpointAutoConfigurationTests.java index bda83d6f2fc..7aa234c1af7 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthMvcEndpointAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthMvcEndpointAutoConfigurationTests.java @@ -16,6 +16,8 @@ package org.springframework.boot.actuate.autoconfigure; +import java.util.Arrays; + import org.junit.After; import org.junit.Test; @@ -34,6 +36,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockServletContext; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import static org.assertj.core.api.Assertions.assertThat; @@ -83,6 +86,20 @@ public class HealthMvcEndpointAutoConfigurationTests { assertThat(map.getDetails().get("foo")).isEqualTo("bar"); } + @Test + public void testSetRoles() throws Exception { + // gh-8314 + this.context = new AnnotationConfigWebApplicationContext(); + this.context.setServletContext(new MockServletContext()); + this.context.register(TestConfiguration.class); + EnvironmentTestUtils.addEnvironment(this.context, + "management.security.roles[0]=super"); + this.context.refresh(); + HealthMvcEndpoint health = this.context.getBean(HealthMvcEndpoint.class); + assertThat(ReflectionTestUtils.getField(health, "roles")) + .isEqualTo(Arrays.asList("super")); + } + @Configuration @ImportAutoConfiguration({ SecurityAutoConfiguration.class, JacksonAutoConfiguration.class, WebMvcAutoConfiguration.class, diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpointTests.java index cca40927dc6..7a49d15d811 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpointTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.endpoint.mvc; +import java.util.Arrays; import java.util.Collections; import javax.servlet.http.HttpServletRequest; @@ -182,6 +183,19 @@ public class HealthMvcEndpointTests { assertThat(((Health) result).getDetails().get("foo")).isNull(); } + @Test + public void customRoleFromListShouldNotExposeDetailsForDefaultRole() { + // gh-8314 + this.mvc = new HealthMvcEndpoint(this.endpoint, true, + Arrays.asList("HERO", "USER")); + given(this.endpoint.invoke()) + .willReturn(new Health.Builder().up().withDetail("foo", "bar").build()); + Object result = this.mvc.invoke(this.hero); + assertThat(result instanceof Health).isTrue(); + assertThat(((Health) result).getStatus() == Status.UP).isTrue(); + assertThat(((Health) result).getDetails().get("foo")).isEqualTo("bar"); + } + @Test public void healthIsCached() { given(this.endpoint.getTimeToLive()).willReturn(10000L); diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptorTests.java index 1aaedad86a3..167ccd35bed 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptorTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -130,11 +130,13 @@ public class MvcEndpointSecurityInterceptorTests { } @Test - public void sensitiveEndpointIfRoleNotCorrectShouldCheckAuthorities() throws Exception { + public void sensitiveEndpointIfRoleNotCorrectShouldCheckAuthorities() + throws Exception { Principal principal = mock(Principal.class); this.request.setUserPrincipal(principal); Authentication authentication = mock(Authentication.class); - Set authorities = Collections.singleton(new SimpleGrantedAuthority("SUPER_HERO")); + Set authorities = Collections + .singleton(new SimpleGrantedAuthority("SUPER_HERO")); doReturn(authorities).when(authentication).getAuthorities(); SecurityContextHolder.getContext().setAuthentication(authentication); assertThat(this.securityInterceptor.preHandle(this.request, this.response, @@ -142,11 +144,13 @@ public class MvcEndpointSecurityInterceptorTests { } @Test - public void sensitiveEndpointIfRoleAndAuthoritiesNotCorrectShouldNotAllowAccess() throws Exception { + public void sensitiveEndpointIfRoleAndAuthoritiesNotCorrectShouldNotAllowAccess() + throws Exception { Principal principal = mock(Principal.class); this.request.setUserPrincipal(principal); Authentication authentication = mock(Authentication.class); - Set authorities = Collections.singleton(new SimpleGrantedAuthority("HERO")); + Set authorities = Collections + .singleton(new SimpleGrantedAuthority("HERO")); doReturn(authorities).when(authentication).getAuthorities(); SecurityContextHolder.getContext().setAuthentication(authentication); assertThat(this.securityInterceptor.preHandle(this.request, this.response, diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/NoSpringSecurityMvcEndpointSecurityInterceptorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/NoSpringSecurityMvcEndpointSecurityInterceptorTests.java index 6d5ad0ceefc..65152658318 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/NoSpringSecurityMvcEndpointSecurityInterceptorTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/NoSpringSecurityMvcEndpointSecurityInterceptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -39,6 +39,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; /** + * Tests for {@link MvcEndpointSecurityInterceptor} when Spring Security is not available. + * * @author Madhura Bhave */ @RunWith(ModifiedClassPathRunner.class) @@ -77,7 +79,8 @@ public class NoSpringSecurityMvcEndpointSecurityInterceptorTests { } @Test - public void sensitiveEndpointIfRoleNotPresentShouldNotValidateAuthorities() throws Exception { + public void sensitiveEndpointIfRoleNotPresentShouldNotValidateAuthorities() + throws Exception { Principal principal = mock(Principal.class); this.request.setUserPrincipal(principal); this.servletContext.declareRoles("HERO"); @@ -105,5 +108,5 @@ public class NoSpringSecurityMvcEndpointSecurityInterceptorTests { } } -} +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java index 5db663d902b..74810e47a2e 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -64,20 +64,33 @@ class DataSourceInitializedPublisher implements BeanPostProcessor { if (bean instanceof JpaProperties) { this.properties = (JpaProperties) bean; } - if (bean instanceof EntityManagerFactory && this.dataSource != null - && isInitializingDatabase()) { - this.applicationContext - .publishEvent(new DataSourceInitializedEvent(this.dataSource)); + if (bean instanceof EntityManagerFactory) { + publishEventIfRequired((EntityManagerFactory) bean); } return bean; } - private boolean isInitializingDatabase() { + private void publishEventIfRequired(EntityManagerFactory entityManagerFactory) { + DataSource dataSource = findDataSource(entityManagerFactory); + if (dataSource != null && isInitializingDatabase(dataSource)) { + this.applicationContext + .publishEvent(new DataSourceInitializedEvent(dataSource)); + } + } + + private DataSource findDataSource(EntityManagerFactory entityManagerFactory) { + Object dataSource = entityManagerFactory.getProperties() + .get("javax.persistence.nonJtaDataSource"); + return (dataSource != null && dataSource instanceof DataSource + ? (DataSource) dataSource : this.dataSource); + } + + private boolean isInitializingDatabase(DataSource dataSource) { if (this.properties == null) { return true; // better safe than sorry } Map hibernate = this.properties - .getHibernateProperties(this.dataSource); + .getHibernateProperties(dataSource); if (hibernate.containsKey("hibernate.hbm2ddl.auto")) { return true; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfigurationTests.java index 050fae10cda..1ad3738297a 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. diff --git a/spring-boot-integration-tests/spring-boot-integration-tests-embedded-servlet-container/src/test/java/org/springframework/boot/context/embedded/ApplicationBuilder.java b/spring-boot-integration-tests/spring-boot-integration-tests-embedded-servlet-container/src/test/java/org/springframework/boot/context/embedded/ApplicationBuilder.java index 67d0a5356e3..8685ff95762 100644 --- a/spring-boot-integration-tests/spring-boot-integration-tests-embedded-servlet-container/src/test/java/org/springframework/boot/context/embedded/ApplicationBuilder.java +++ b/spring-boot-integration-tests/spring-boot-integration-tests-embedded-servlet-container/src/test/java/org/springframework/boot/context/embedded/ApplicationBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractDependencyFilterMojo.java b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractDependencyFilterMojo.java index 0cb0ee4a274..bcb5b2dbedc 100644 --- a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractDependencyFilterMojo.java +++ b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractDependencyFilterMojo.java @@ -16,7 +16,7 @@ package org.springframework.boot.maven; -import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; @@ -87,29 +87,15 @@ public abstract class AbstractDependencyFilterMojo extends AbstractMojo { this.excludeArtifactIds = excludeArtifactIds; } - @SuppressWarnings("unchecked") protected Set filterDependencies(Set dependencies, FilterArtifacts filters) throws MojoExecutionException { - List artifactsFilters = filters.getFilters(); try { - for (ArtifactsFilter filter : artifactsFilters) { - Set result = filter.filter(dependencies); - applyFiltering(dependencies, result); - } - return dependencies; - } - catch (ArtifactFilterException e) { - throw new MojoExecutionException(e.getMessage(), e); + Set filtered = new LinkedHashSet(dependencies); + filtered.retainAll(filters.filter(dependencies)); + return filtered; } - } - - private void applyFiltering(Set original, Set filtered) { - Iterator iterator = original.iterator(); - while (iterator.hasNext()) { - Artifact element = iterator.next(); - if (!filtered.contains(element)) { - iterator.remove(); - } + catch (ArtifactFilterException ex) { + throw new MojoExecutionException(ex.getMessage(), ex); } } diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/DependencyFilterMojoTests.java b/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/DependencyFilterMojoTests.java index daa49766ae0..212e711d12a 100644 --- a/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/DependencyFilterMojoTests.java +++ b/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/DependencyFilterMojoTests.java @@ -103,7 +103,7 @@ public class DependencyFilterMojoTests { } @Test - public void filterExcludeKeepOrder() throws MojoExecutionException { + public void filterExcludeKeepOrder() throws MojoExecutionException { Exclude exclude = new Exclude(); exclude.setGroupId("com.bar"); exclude.setArtifactId("two"); @@ -121,7 +121,8 @@ public class DependencyFilterMojoTests { return createArtifact(groupId, artifactId, null); } - private static Artifact createArtifact(String groupId, String artifactId, String scope) { + private static Artifact createArtifact(String groupId, String artifactId, + String scope) { Artifact a = mock(Artifact.class); given(a.getGroupId()).willReturn(groupId); given(a.getArtifactId()).willReturn(artifactId); diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainer.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainer.java index bd722a09621..063d6542bec 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainer.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainer.java @@ -205,9 +205,6 @@ public class JettyEmbeddedServletContainer implements EmbeddedWebServer { @Override public void stop() { synchronized (this.monitor) { - if (!this.started) { - return; - } this.started = false; try { this.server.stop(); diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainer.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainer.java index 75689a0dffe..1b5052b3d8a 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainer.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainer.java @@ -90,32 +90,38 @@ public class TomcatEmbeddedServletContainer implements EmbeddedWebServer { synchronized (this.monitor) { try { addInstanceIdToEngineName(); + try { + // Remove service connectors to that protocol binding doesn't happen + // yet + removeServiceConnectors(); - // Remove service connectors to that protocol binding doesn't happen yet - removeServiceConnectors(); + // Start the server to trigger initialization listeners + this.tomcat.start(); - // Start the server to trigger initialization listeners - this.tomcat.start(); + // We can re-throw failure exception directly in the main thread + rethrowDeferredStartupExceptions(); - // We can re-throw failure exception directly in the main thread - rethrowDeferredStartupExceptions(); + Context context = findContext(); + try { + ContextBindings.bindClassLoader(context, context.getNamingToken(), + getClass().getClassLoader()); + } + catch (NamingException ex) { + // Naming is not enabled. Continue + } - Context context = findContext(); - try { - ContextBindings.bindClassLoader(context, context.getNamingToken(), - getClass().getClassLoader()); + // Unlike Jetty, all Tomcat threads are daemon threads. We create a + // blocking non-daemon to stop immediate shutdown + startDaemonAwaitThread(); } - catch (NamingException ex) { - // Naming is not enabled. Continue + catch (Exception ex) { + containerCounter.decrementAndGet(); + throw ex; } - - // Unlike Jetty, all Tomcat threads are daemon threads. We create a - // blocking non-daemon to stop immediate shutdown - startDaemonAwaitThread(); } catch (Exception ex) { - throw new EmbeddedWebServerException( - "Unable to start embedded Tomcat", ex); + throw new EmbeddedWebServerException("Unable to start embedded Tomcat", + ex); } } } @@ -279,9 +285,7 @@ public class TomcatEmbeddedServletContainer implements EmbeddedWebServer { @Override public void stop() throws EmbeddedWebServerException { synchronized (this.monitor) { - if (!this.started) { - return; - } + boolean wasStarted = this.started; try { this.started = false; try { @@ -293,11 +297,13 @@ public class TomcatEmbeddedServletContainer implements EmbeddedWebServer { } } catch (Exception ex) { - throw new EmbeddedWebServerException( - "Unable to stop embedded Tomcat", ex); + throw new EmbeddedWebServerException("Unable to stop embedded Tomcat", + ex); } finally { - containerCounter.decrementAndGet(); + if (wasStarted) { + containerCounter.decrementAndGet(); + } } } } diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java index b653f449205..8ab2157bb39 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java @@ -174,6 +174,16 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { assertThat(this.output.toString()).containsOnlyOnce("started on port"); } + @Test + public void stopCalledTwice() throws Exception { + AbstractEmbeddedServletContainerFactory factory = getFactory(); + this.container = factory + .getEmbeddedServletContainer(exampleServletRegistration()); + this.container.start(); + this.container.stop(); + this.container.stop(); + } + @Test public void emptyServerWhenPortIsMinusOne() throws Exception { AbstractEmbeddedServletContainerFactory factory = getFactory(); @@ -315,16 +325,6 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { getFactory().setContextPath("/"); } - @Test - public void doubleStop() throws Exception { - AbstractEmbeddedServletContainerFactory factory = getFactory(); - this.container = factory - .getEmbeddedServletContainer(exampleServletRegistration()); - this.container.start(); - this.container.stop(); - this.container.stop(); - } - @Test public void multipleConfigurations() throws Exception { AbstractEmbeddedServletContainerFactory factory = getFactory(); diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactoryTests.java index 7ce4de1aeb3..0b27598762c 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactoryTests.java @@ -143,6 +143,16 @@ public class JettyEmbeddedServletContainerFactoryTests .isEmpty(); } + @Test + public void stopCalledWithoutStart() throws Exception { + JettyEmbeddedServletContainerFactory factory = getFactory(); + this.container = factory + .getEmbeddedServletContainer(exampleServletRegistration()); + this.container.stop(); + Server server = ((JettyEmbeddedServletContainer) this.container).getServer(); + assertThat(server.isStopped()).isTrue(); + } + @Override protected void addConnector(final int port, AbstractEmbeddedServletContainerFactory factory) { diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java index adb88202e7c..c63b31e8b9c 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java @@ -352,6 +352,16 @@ public class TomcatEmbeddedServletContainerFactoryTests .doesNotContain("appears to have started a thread named [main]"); } + @Test + public void stopCalledWithoutStart() throws Exception { + TomcatEmbeddedServletContainerFactory factory = getFactory(); + this.container = factory + .getEmbeddedServletContainer(exampleServletRegistration()); + this.container.stop(); + Tomcat tomcat = ((TomcatEmbeddedServletContainer) this.container).getTomcat(); + assertThat(tomcat.getServer().getState()).isSameAs(LifecycleState.DESTROYED); + } + @Override protected void addConnector(int port, AbstractEmbeddedServletContainerFactory factory) {