From caf46043c64313dcae380702dcc403f47a093f81 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sat, 5 Nov 2022 16:21:59 -0700 Subject: [PATCH] Add @DirtiesUrlFactories annotation Add `@DirtiesUrlFactories` annotation that can be used to reset URL factories. Closes gh-33017 --- .../web/servlet/DirtiesUrlFactories.java | 39 +++++++++++++ .../servlet/DirtiesUrlFactoriesExtension.java | 56 +++++++++++++++++++ .../tomcat/SslConnectorCustomizerTests.java | 7 +-- .../AbstractServletWebServerFactoryTests.java | 18 +----- 4 files changed, 99 insertions(+), 21 deletions(-) create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/web/servlet/DirtiesUrlFactories.java create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/web/servlet/DirtiesUrlFactoriesExtension.java diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/web/servlet/DirtiesUrlFactories.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/web/servlet/DirtiesUrlFactories.java new file mode 100644 index 00000000000..deb5b59e761 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/web/servlet/DirtiesUrlFactories.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.testsupport.web.servlet; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Indicates that a test pollutes URL factories. + * + * @author Phillip Webb + * @since 2.6.14 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Documented +@ExtendWith(DirtiesUrlFactoriesExtension.class) +public @interface DirtiesUrlFactories { + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/web/servlet/DirtiesUrlFactoriesExtension.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/web/servlet/DirtiesUrlFactoriesExtension.java new file mode 100644 index 00000000000..db2e102e284 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/web/servlet/DirtiesUrlFactoriesExtension.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.testsupport.web.servlet; + +import java.net.URL; + +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.util.ClassUtils; + +/** + * JUnit extension used by {@link DirtiesUrlFactories @DirtiesUrlFactories}. + * + * @author Phillip Webb + */ +class DirtiesUrlFactoriesExtension implements BeforeEachCallback, AfterEachCallback { + + private static final String TOMCAT_URL_STREAM_HANDLER_FACTORY = "org.apache.catalina.webresources.TomcatURLStreamHandlerFactory"; + + @Override + public void afterEach(ExtensionContext context) throws Exception { + reset(); + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + reset(); + } + + private void reset() { + ClassLoader classLoader = getClass().getClassLoader(); + if (ClassUtils.isPresent(TOMCAT_URL_STREAM_HANDLER_FACTORY, classLoader)) { + Class factoryClass = ClassUtils.resolveClassName(TOMCAT_URL_STREAM_HANDLER_FACTORY, classLoader); + ReflectionTestUtils.setField(factoryClass, "instance", null); + } + ReflectionTestUtils.setField(URL.class, "factory", null); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizerTests.java index 7c0deaa9851..e19581f8698 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizerTests.java @@ -18,7 +18,6 @@ package org.springframework.boot.web.embedded.tomcat; import java.io.IOException; import java.io.InputStream; -import java.net.URL; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; @@ -27,7 +26,6 @@ import java.security.cert.CertificateException; import org.apache.catalina.LifecycleState; import org.apache.catalina.connector.Connector; import org.apache.catalina.startup.Tomcat; -import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; import org.apache.coyote.http11.Http11NioProtocol; import org.apache.tomcat.util.net.SSLHostConfig; import org.junit.jupiter.api.AfterEach; @@ -37,12 +35,12 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.testsupport.system.CapturedOutput; import org.springframework.boot.testsupport.system.OutputCaptureExtension; +import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.SslStoreProvider; import org.springframework.boot.web.server.WebServerException; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; -import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -55,6 +53,7 @@ import static org.mockito.Mockito.mock; * @author Brian Clozel */ @ExtendWith(OutputCaptureExtension.class) +@DirtiesUrlFactories class SslConnectorCustomizerTests { private Tomcat tomcat; @@ -72,8 +71,6 @@ class SslConnectorCustomizerTests { @AfterEach void stop() throws Exception { System.clearProperty("javax.net.ssl.trustStorePassword"); - ReflectionTestUtils.setField(TomcatURLStreamHandlerFactory.class, "instance", null); - ReflectionTestUtils.setField(URL.class, "factory", null); this.tomcat.stop(); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java index e1c39b5ad6e..5bd12602e5d 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java @@ -77,7 +77,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.InputStreamFactory; @@ -112,6 +111,7 @@ import org.springframework.boot.system.ApplicationHome; import org.springframework.boot.system.ApplicationTemp; import org.springframework.boot.testsupport.system.CapturedOutput; import org.springframework.boot.testsupport.system.OutputCaptureExtension; +import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; import org.springframework.boot.testsupport.web.servlet.ExampleFilter; import org.springframework.boot.testsupport.web.servlet.ExampleServlet; import org.springframework.boot.web.server.Compression; @@ -140,7 +140,6 @@ import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.ClassUtils; import org.springframework.util.FileCopyUtils; import org.springframework.util.StreamUtils; @@ -167,6 +166,7 @@ import static org.mockito.Mockito.mock; * @author Raja Kolli */ @ExtendWith(OutputCaptureExtension.class) +@DirtiesUrlFactories public abstract class AbstractServletWebServerFactoryTests { @TempDir @@ -205,20 +205,6 @@ public abstract class AbstractServletWebServerFactoryTests { // Ignore } } - if (ClassUtils.isPresent("org.apache.catalina.webresources.TomcatURLStreamHandlerFactory", - getClass().getClassLoader())) { - ReflectionTestUtils.setField(TomcatURLStreamHandlerFactory.class, "instance", null); - } - ReflectionTestUtils.setField(URL.class, "factory", null); - } - - @AfterEach - void clearUrlStreamHandlerFactory() { - if (ClassUtils.isPresent("org.apache.catalina.webresources.TomcatURLStreamHandlerFactory", - getClass().getClassLoader())) { - ReflectionTestUtils.setField(TomcatURLStreamHandlerFactory.class, "instance", null); - ReflectionTestUtils.setField(URL.class, "factory", null); - } } @Test