Create spring-boot-web-server module

This commit is contained in:
Andy Wilkinson
2025-05-01 13:40:49 +01:00
committed by Phillip Webb
parent 0cf76bf43b
commit 96bee8e034
202 changed files with 374 additions and 271 deletions

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2012-2020 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.web.server;
import org.apache.coyote.http11.Http11NioProtocol;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link Compression}.
*
* @author Andy Wilkinson
*/
class CompressionTests {
@Test
void defaultCompressibleMimeTypesMatchesTomcatsDefault() {
assertThat(new Compression().getMimeTypes()).containsExactlyInAnyOrder(getTomcatDefaultCompressibleMimeTypes());
}
private String[] getTomcatDefaultCompressibleMimeTypes() {
Http11NioProtocol protocol = new Http11NioProtocol();
return protocol.getCompressibleMimeTypes();
}
}

View File

@@ -0,0 +1,235 @@
/*
* Copyright 2012-2024 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.web.server;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Pattern;
import org.apache.catalina.startup.Tomcat;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.boot.web.server.MimeMappings.DefaultMimeMappings;
import org.springframework.boot.web.server.MimeMappings.Mapping;
import org.springframework.boot.web.server.MimeMappings.MimeMappingsRuntimeHints;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link MimeMappings}.
*
* @author Phillip Webb
* @author Guirong Hu
*/
class MimeMappingsTests {
@Test
void defaultsCannotBeModified() {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(() -> MimeMappings.DEFAULT.add("foo", "foo/bar"));
}
@Test
void createFromExisting() {
MimeMappings mappings = new MimeMappings();
mappings.add("foo", "bar");
MimeMappings clone = new MimeMappings(mappings);
mappings.add("baz", "bar");
assertThat(clone.get("foo")).isEqualTo("bar");
assertThat(clone.get("baz")).isNull();
}
@Test
void createFromMap() {
Map<String, String> mappings = new HashMap<>();
mappings.put("foo", "bar");
MimeMappings clone = new MimeMappings(mappings);
mappings.put("baz", "bar");
assertThat(clone.get("foo")).isEqualTo("bar");
assertThat(clone.get("baz")).isNull();
}
@Test
void iterate() {
MimeMappings mappings = new MimeMappings();
mappings.add("foo", "bar");
mappings.add("baz", "boo");
List<MimeMappings.Mapping> mappingList = new ArrayList<>();
for (MimeMappings.Mapping mapping : mappings) {
mappingList.add(mapping);
}
assertThat(mappingList.get(0).getExtension()).isEqualTo("foo");
assertThat(mappingList.get(0).getMimeType()).isEqualTo("bar");
assertThat(mappingList.get(1).getExtension()).isEqualTo("baz");
assertThat(mappingList.get(1).getMimeType()).isEqualTo("boo");
}
@Test
void getAll() {
MimeMappings mappings = new MimeMappings();
mappings.add("foo", "bar");
mappings.add("baz", "boo");
List<MimeMappings.Mapping> mappingList = new ArrayList<>(mappings.getAll());
assertThat(mappingList.get(0).getExtension()).isEqualTo("foo");
assertThat(mappingList.get(0).getMimeType()).isEqualTo("bar");
assertThat(mappingList.get(1).getExtension()).isEqualTo("baz");
assertThat(mappingList.get(1).getMimeType()).isEqualTo("boo");
}
@Test
void addNew() {
MimeMappings mappings = new MimeMappings();
assertThat(mappings.add("foo", "bar")).isNull();
}
@Test
void addReplacesExisting() {
MimeMappings mappings = new MimeMappings();
mappings.add("foo", "bar");
assertThat(mappings.add("foo", "baz")).isEqualTo("bar");
}
@Test
void remove() {
MimeMappings mappings = new MimeMappings();
mappings.add("foo", "bar");
assertThat(mappings.remove("foo")).isEqualTo("bar");
assertThat(mappings.remove("foo")).isNull();
}
@Test
void get() {
MimeMappings mappings = new MimeMappings();
mappings.add("foo", "bar");
assertThat(mappings.get("foo")).isEqualTo("bar");
}
@Test
void getMissing() {
MimeMappings mappings = new MimeMappings();
assertThat(mappings.get("foo")).isNull();
}
@Test
void makeUnmodifiable() {
MimeMappings mappings = new MimeMappings();
mappings.add("foo", "bar");
MimeMappings unmodifiable = MimeMappings.unmodifiableMappings(mappings);
try {
unmodifiable.remove("foo");
}
catch (UnsupportedOperationException ex) {
// Expected
}
mappings.remove("foo");
assertThat(unmodifiable.get("foo")).isNull();
}
@Test
void mimeTypesInDefaultMappingsAreCorrectlyStructured() {
String regName = "[A-Za-z0-9!#$&.+\\-^_]{1,127}";
Pattern pattern = Pattern.compile("^" + regName + "/" + regName + "$");
assertThat(MimeMappings.DEFAULT).allSatisfy((mapping) -> assertThat(mapping.getMimeType()).matches(pattern));
}
@Test
void getCommonTypeOnDefaultMimeMappingsDoesNotLoadMappings() {
DefaultMimeMappings mappings = new DefaultMimeMappings();
assertThat(mappings.get("json")).isEqualTo("application/json");
assertThat((Object) mappings).extracting("loaded").isNull();
}
@Test
void getExoticTypeOnDefaultMimeMappingsLoadsMappings() {
DefaultMimeMappings mappings = new DefaultMimeMappings();
assertThat(mappings.get("123")).isEqualTo("application/vnd.lotus-1-2-3");
assertThat((Object) mappings).extracting("loaded").isNotNull();
}
@Test
void iterateOnDefaultMimeMappingsLoadsMappings() {
DefaultMimeMappings mappings = new DefaultMimeMappings();
assertThat(mappings).isNotEmpty();
assertThat((Object) mappings).extracting("loaded").isNotNull();
}
@Test
void commonMappingsAreSubsetOfAllMappings() {
MimeMappings defaultMappings = new DefaultMimeMappings();
MimeMappings commonMappings = (MimeMappings) ReflectionTestUtils.getField(DefaultMimeMappings.class, "COMMON");
for (Mapping commonMapping : commonMappings) {
assertThat(defaultMappings.get(commonMapping.getExtension())).isEqualTo(commonMapping.getMimeType());
}
}
@Test
void lazyCopyWhenNotMutatedDelegates() {
DefaultMimeMappings mappings = new DefaultMimeMappings();
MimeMappings lazyCopy = MimeMappings.lazyCopy(mappings);
assertThat(lazyCopy.get("json")).isEqualTo("application/json");
assertThat((Object) mappings).extracting("loaded").isNull();
}
@Test
void lazyCopyWhenMutatedCreatesCopy() {
DefaultMimeMappings mappings = new DefaultMimeMappings();
MimeMappings lazyCopy = MimeMappings.lazyCopy(mappings);
lazyCopy.add("json", "other/json");
assertThat(lazyCopy.get("json")).isEqualTo("other/json");
assertThat((Object) mappings).extracting("loaded").isNotNull();
}
@Test
void lazyCopyWhenMutatedCreatesCopyOnlyOnce() {
MimeMappings mappings = new MimeMappings();
mappings.add("json", "one/json");
MimeMappings lazyCopy = MimeMappings.lazyCopy(mappings);
lazyCopy.add("first", "copy/yes");
assertThat(lazyCopy.get("json")).isEqualTo("one/json");
mappings.add("json", "two/json");
lazyCopy.add("second", "copy/no");
assertThat(lazyCopy.get("json")).isEqualTo("one/json");
}
@Test
void mimeMappingsMatchesTomcatDefaults() throws IOException {
Properties ourDefaultMimeMappings = PropertiesLoaderUtils
.loadProperties(new ClassPathResource("mime-mappings.properties", getClass()));
Properties tomcatDefaultMimeMappings = PropertiesLoaderUtils
.loadProperties(new ClassPathResource("MimeTypeMappings.properties", Tomcat.class));
assertThat(ourDefaultMimeMappings).containsExactlyInAnyOrderEntriesOf(tomcatDefaultMimeMappings);
}
@Test
void shouldRegisterHints() {
RuntimeHints runtimeHints = new RuntimeHints();
new MimeMappingsRuntimeHints().registerHints(runtimeHints, getClass().getClassLoader());
assertThat(RuntimeHintsPredicates.resource()
.forResource("org/springframework/boot/web/server/mime-mappings.properties")).accepts(runtimeHints);
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright 2012-2025 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.web.server;
import java.util.Iterator;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.testsupport.classpath.resources.WithResource;
import org.springframework.boot.web.context.reactive.ReactiveWebApplicationContext;
import org.springframework.boot.web.context.reactive.StandardReactiveWebEnvironment;
import org.springframework.boot.web.server.reactive.MockReactiveWebServerFactory;
import org.springframework.boot.web.server.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
import org.springframework.boot.web.server.servlet.MockServletWebServerFactory;
import org.springframework.boot.web.server.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.PropertySource;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.test.context.support.TestPropertySourceUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.StandardServletEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SpringApplication} with a {@link WebServer}.
*
* @author Andy wilkinson
*/
class SpringApplicationWebServerTests {
private String headlessProperty;
private ConfigurableApplicationContext context;
@BeforeEach
void storeAndClearHeadlessProperty() {
this.headlessProperty = System.getProperty("java.awt.headless");
System.clearProperty("java.awt.headless");
}
@AfterEach
void reinstateHeadlessProperty() {
if (this.headlessProperty == null) {
System.clearProperty("java.awt.headless");
}
else {
System.setProperty("java.awt.headless", this.headlessProperty);
}
}
@AfterEach
void cleanUp() {
if (this.context != null) {
this.context.close();
}
System.clearProperty("spring.main.banner-mode");
}
@Test
void defaultApplicationContextForWeb() {
SpringApplication application = new SpringApplication(ExampleWebConfig.class);
application.setWebApplicationType(WebApplicationType.SERVLET);
this.context = application.run();
assertThat(this.context).isInstanceOf(AnnotationConfigServletWebServerApplicationContext.class);
}
@Test
void defaultApplicationContextForReactiveWeb() {
SpringApplication application = new SpringApplication(ExampleReactiveWebConfig.class);
application.setWebApplicationType(WebApplicationType.REACTIVE);
this.context = application.run();
assertThat(this.context).isInstanceOf(AnnotationConfigReactiveWebServerApplicationContext.class);
}
@Test
void environmentForWeb() {
SpringApplication application = new SpringApplication(ExampleWebConfig.class);
application.setWebApplicationType(WebApplicationType.SERVLET);
this.context = application.run();
assertThat(this.context.getEnvironment()).isInstanceOf(StandardServletEnvironment.class);
assertThat(this.context.getEnvironment().getClass().getName()).endsWith("ApplicationServletEnvironment");
}
@Test
void environmentForReactiveWeb() {
SpringApplication application = new SpringApplication(ExampleReactiveWebConfig.class);
application.setWebApplicationType(WebApplicationType.REACTIVE);
this.context = application.run();
assertThat(this.context.getEnvironment()).isInstanceOf(StandardReactiveWebEnvironment.class);
assertThat(this.context.getEnvironment().getClass().getName()).endsWith("ApplicationReactiveWebEnvironment");
}
@Test
void webApplicationConfiguredViaAPropertyHasTheCorrectTypeOfContextAndEnvironment() {
ConfigurableApplicationContext context = new SpringApplication(ExampleWebConfig.class)
.run("--spring.main.web-application-type=servlet");
assertThat(context).isInstanceOf(WebApplicationContext.class);
assertThat(context.getEnvironment()).isInstanceOf(StandardServletEnvironment.class);
assertThat(context.getEnvironment().getClass().getName()).endsWith("ApplicationServletEnvironment");
}
@Test
void reactiveApplicationConfiguredViaAPropertyHasTheCorrectTypeOfContextAndEnvironment() {
ConfigurableApplicationContext context = new SpringApplication(ExampleReactiveWebConfig.class)
.run("--spring.main.web-application-type=reactive");
assertThat(context).isInstanceOf(ReactiveWebApplicationContext.class);
assertThat(context.getEnvironment()).isInstanceOf(StandardReactiveWebEnvironment.class);
assertThat(context.getEnvironment().getClass().getName()).endsWith("ApplicationReactiveWebEnvironment");
}
@Test
@WithResource(name = "application-withwebapplicationtype.properties",
content = "spring.main.web-application-type=reactive")
void environmentIsConvertedIfTypeDoesNotMatch() {
ConfigurableApplicationContext context = new SpringApplication(ExampleReactiveWebConfig.class)
.run("--spring.profiles.active=withwebapplicationtype");
assertThat(context).isInstanceOf(ReactiveWebApplicationContext.class);
assertThat(context.getEnvironment()).isInstanceOf(StandardReactiveWebEnvironment.class);
assertThat(context.getEnvironment().getClass().getName()).endsWith("ApplicationReactiveWebEnvironment");
}
@Test
void webApplicationSwitchedOffInListener() {
SpringApplication application = new SpringApplication(ExampleWebConfig.class);
application.addListeners((ApplicationListener<ApplicationEnvironmentPreparedEvent>) (event) -> {
assertThat(event.getEnvironment().getClass().getName()).endsWith("ApplicationServletEnvironment");
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(event.getEnvironment(), "foo=bar");
event.getSpringApplication().setWebApplicationType(WebApplicationType.NONE);
});
this.context = application.run();
assertThat(this.context.getEnvironment()).isNotInstanceOf(StandardServletEnvironment.class);
assertThat(this.context.getEnvironment().getProperty("foo")).isEqualTo("bar");
Iterator<PropertySource<?>> iterator = this.context.getEnvironment().getPropertySources().iterator();
assertThat(iterator.next().getName()).isEqualTo("configurationProperties");
assertThat(iterator.next().getName())
.isEqualTo(TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME);
}
@Configuration(proxyBeanMethods = false)
static class ExampleWebConfig {
@Bean
MockServletWebServerFactory webServer() {
return new MockServletWebServerFactory();
}
}
@Configuration(proxyBeanMethods = false)
static class ExampleReactiveWebConfig {
@Bean
MockReactiveWebServerFactory webServerFactory() {
return new MockReactiveWebServerFactory();
}
@Bean
HttpHandler httpHandler() {
return (serverHttpRequest, serverHttpResponse) -> Mono.empty();
}
}
}

View File

@@ -0,0 +1,201 @@
/*
* Copyright 2012-2025 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.web.server;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link WebServerFactoryCustomizerBeanPostProcessor}.
*
* @author Phillip Webb
*/
@ExtendWith(MockitoExtension.class)
class WebServerFactoryCustomizerBeanPostProcessorTests {
private final WebServerFactoryCustomizerBeanPostProcessor processor = new WebServerFactoryCustomizerBeanPostProcessor();
@Mock
private ListableBeanFactory beanFactory;
@BeforeEach
void setup() {
this.processor.setBeanFactory(this.beanFactory);
}
@Test
void setBeanFactoryWhenNotListableShouldThrowException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.processor.setBeanFactory(mock(BeanFactory.class)))
.withMessageContaining("'beanFactory' must be a ListableBeanFactory");
}
@Test
void postProcessBeforeShouldReturnBean() {
Object bean = new Object();
Object result = this.processor.postProcessBeforeInitialization(bean, "foo");
assertThat(result).isSameAs(bean);
}
@Test
void postProcessAfterShouldReturnBean() {
Object bean = new Object();
Object result = this.processor.postProcessAfterInitialization(bean, "foo");
assertThat(result).isSameAs(bean);
}
@Test
void postProcessAfterShouldCallInterfaceCustomizers() {
Map<String, Object> beans = addInterfaceBeans();
addMockBeans(beans);
postProcessBeforeInitialization(WebServerFactory.class);
assertThat(wasCalled(beans, "one")).isFalse();
assertThat(wasCalled(beans, "two")).isFalse();
assertThat(wasCalled(beans, "all")).isTrue();
}
@Test
void postProcessAfterWhenWebServerFactoryOneShouldCallInterfaceCustomizers() {
Map<String, Object> beans = addInterfaceBeans();
addMockBeans(beans);
postProcessBeforeInitialization(WebServerFactoryOne.class);
assertThat(wasCalled(beans, "one")).isTrue();
assertThat(wasCalled(beans, "two")).isFalse();
assertThat(wasCalled(beans, "all")).isTrue();
}
@Test
void postProcessAfterWhenWebServerFactoryTwoShouldCallInterfaceCustomizers() {
Map<String, Object> beans = addInterfaceBeans();
addMockBeans(beans);
postProcessBeforeInitialization(WebServerFactoryTwo.class);
assertThat(wasCalled(beans, "one")).isFalse();
assertThat(wasCalled(beans, "two")).isTrue();
assertThat(wasCalled(beans, "all")).isTrue();
}
private Map<String, Object> addInterfaceBeans() {
WebServerFactoryOneCustomizer oneCustomizer = new WebServerFactoryOneCustomizer();
WebServerFactoryTwoCustomizer twoCustomizer = new WebServerFactoryTwoCustomizer();
WebServerFactoryAllCustomizer allCustomizer = new WebServerFactoryAllCustomizer();
Map<String, Object> beans = new LinkedHashMap<>();
beans.put("one", oneCustomizer);
beans.put("two", twoCustomizer);
beans.put("all", allCustomizer);
return beans;
}
@Test
void postProcessAfterShouldCallLambdaCustomizers() {
List<String> called = new ArrayList<>();
addLambdaBeans(called);
postProcessBeforeInitialization(WebServerFactory.class);
assertThat(called).containsExactly("all");
}
@Test
void postProcessAfterWhenWebServerFactoryOneShouldCallLambdaCustomizers() {
List<String> called = new ArrayList<>();
addLambdaBeans(called);
postProcessBeforeInitialization(WebServerFactoryOne.class);
assertThat(called).containsExactly("one", "all");
}
@Test
void postProcessAfterWhenWebServerFactoryTwoShouldCallLambdaCustomizers() {
List<String> called = new ArrayList<>();
addLambdaBeans(called);
postProcessBeforeInitialization(WebServerFactoryTwo.class);
assertThat(called).containsExactly("two", "all");
}
private void addLambdaBeans(List<String> called) {
WebServerFactoryCustomizer<WebServerFactoryOne> one = (f) -> called.add("one");
WebServerFactoryCustomizer<WebServerFactoryTwo> two = (f) -> called.add("two");
WebServerFactoryCustomizer<WebServerFactory> all = (f) -> called.add("all");
Map<String, Object> beans = new LinkedHashMap<>();
beans.put("one", one);
beans.put("two", two);
beans.put("all", all);
addMockBeans(beans);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void addMockBeans(Map<String, ?> beans) {
given(this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false))
.willReturn((Map<String, WebServerFactoryCustomizer>) beans);
}
private void postProcessBeforeInitialization(Class<?> type) {
this.processor.postProcessBeforeInitialization(mock(type), "foo");
}
private boolean wasCalled(Map<String, ?> beans, String name) {
return ((MockWebServerFactoryCustomizer<?>) beans.get(name)).wasCalled();
}
interface WebServerFactoryOne extends WebServerFactory {
}
interface WebServerFactoryTwo extends WebServerFactory {
}
static class MockWebServerFactoryCustomizer<T extends WebServerFactory> implements WebServerFactoryCustomizer<T> {
private boolean called;
@Override
public void customize(T factory) {
this.called = true;
}
boolean wasCalled() {
return this.called;
}
}
static class WebServerFactoryOneCustomizer extends MockWebServerFactoryCustomizer<WebServerFactoryOne> {
}
static class WebServerFactoryTwoCustomizer extends MockWebServerFactoryCustomizer<WebServerFactoryTwo> {
}
static class WebServerFactoryAllCustomizer extends MockWebServerFactoryCustomizer<WebServerFactory> {
}
}

View File

@@ -0,0 +1,206 @@
/*
* Copyright 2012-2025 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.web.server;
import org.junit.jupiter.api.Test;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslBundleKey;
import org.springframework.boot.ssl.SslOptions;
import org.springframework.boot.ssl.SslStoreBundle;
import org.springframework.boot.testsupport.classpath.resources.ResourcePath;
import org.springframework.boot.testsupport.classpath.resources.WithPackageResources;
import org.springframework.boot.testsupport.ssl.MockPkcs11Security;
import org.springframework.boot.testsupport.ssl.MockPkcs11SecurityProvider;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link WebServerSslBundle}.
*
* @author Scott Frederick
* @author Phillip Webb
* @author Moritz Halbritter
*/
@MockPkcs11Security
class WebServerSslBundleTests {
@Test
void whenSslDisabledThrowsException() {
Ssl ssl = new Ssl();
ssl.setEnabled(false);
assertThatIllegalStateException().isThrownBy(() -> WebServerSslBundle.get(ssl))
.withMessage("SSL is not enabled");
}
@Test
@WithPackageResources("test.p12")
void whenFromJksProperties() {
Ssl ssl = new Ssl();
ssl.setKeyStore("classpath:test.p12");
ssl.setKeyStorePassword("secret");
ssl.setKeyStoreType("PKCS12");
ssl.setTrustStore("classpath:test.p12");
ssl.setTrustStorePassword("secret");
ssl.setTrustStoreType("PKCS12");
ssl.setKeyPassword("password");
ssl.setKeyAlias("alias");
ssl.setClientAuth(Ssl.ClientAuth.NONE);
ssl.setCiphers(new String[] { "ONE", "TWO", "THREE" });
ssl.setEnabledProtocols(new String[] { "TLSv1.1", "TLSv1.2" });
ssl.setProtocol("TestProtocol");
SslBundle bundle = WebServerSslBundle.get(ssl);
assertThat(bundle).isNotNull();
assertThat(bundle.getProtocol()).isEqualTo("TestProtocol");
SslBundleKey key = bundle.getKey();
assertThat(key.getPassword()).isEqualTo("password");
assertThat(key.getAlias()).isEqualTo("alias");
SslStoreBundle stores = bundle.getStores();
assertThat(stores.getKeyStorePassword()).isEqualTo("secret");
assertThat(stores.getKeyStore()).isNotNull();
assertThat(stores.getTrustStore()).isNotNull();
SslOptions options = bundle.getOptions();
assertThat(options.getCiphers()).containsExactly("ONE", "TWO", "THREE");
assertThat(options.getEnabledProtocols()).containsExactly("TLSv1.1", "TLSv1.2");
}
@Test
@WithPackageResources("test.jks")
void whenFromJksPropertiesWithPkcs11StoreType(@ResourcePath("test.jks") String keyStorePath) {
Ssl ssl = new Ssl();
ssl.setKeyStoreType("PKCS11");
ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME);
ssl.setKeyStore(keyStorePath);
ssl.setKeyPassword("password");
ssl.setClientAuth(Ssl.ClientAuth.NONE);
assertThatIllegalStateException().isThrownBy(() -> WebServerSslBundle.get(ssl))
.withMessageContaining("must be empty or null for PKCS11 hardware key stores");
}
@Test
void whenFromPkcs11Properties() {
Ssl ssl = new Ssl();
ssl.setKeyStoreType("PKCS11");
ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME);
ssl.setTrustStoreType("PKCS11");
ssl.setTrustStoreProvider(MockPkcs11SecurityProvider.NAME);
ssl.setKeyPassword("password");
ssl.setClientAuth(Ssl.ClientAuth.NONE);
SslBundle bundle = WebServerSslBundle.get(ssl);
assertThat(bundle).isNotNull();
assertThat(bundle.getProtocol()).isEqualTo("TLS");
SslBundleKey key = bundle.getKey();
assertThat(key.getPassword()).isEqualTo("password");
SslStoreBundle stores = bundle.getStores();
assertThat(stores.getKeyStore()).isNotNull();
assertThat(stores.getTrustStore()).isNotNull();
}
@Test
@WithPackageResources({ "test-cert.pem", "test-key.pem", "test-cert-chain.pem" })
void whenFromPemProperties() {
Ssl ssl = new Ssl();
ssl.setCertificate("classpath:test-cert.pem");
ssl.setCertificatePrivateKey("classpath:test-key.pem");
ssl.setTrustCertificate("classpath:test-cert-chain.pem");
ssl.setKeyStoreType("PKCS12");
ssl.setTrustStoreType("PKCS12");
ssl.setKeyPassword("password");
ssl.setClientAuth(Ssl.ClientAuth.NONE);
ssl.setCiphers(new String[] { "ONE", "TWO", "THREE" });
ssl.setEnabledProtocols(new String[] { "TLSv1.1", "TLSv1.2" });
ssl.setProtocol("TLSv1.1");
SslBundle bundle = WebServerSslBundle.get(ssl);
assertThat(bundle).isNotNull();
SslBundleKey key = bundle.getKey();
assertThat(key.getAlias()).isNull();
assertThat(key.getPassword()).isEqualTo("password");
SslStoreBundle stores = bundle.getStores();
assertThat(stores.getKeyStorePassword()).isNull();
assertThat(stores.getKeyStore()).isNotNull();
assertThat(stores.getTrustStore()).isNotNull();
SslOptions options = bundle.getOptions();
assertThat(options.getCiphers()).containsExactly("ONE", "TWO", "THREE");
assertThat(options.getEnabledProtocols()).containsExactly("TLSv1.1", "TLSv1.2");
}
@Test
@WithPackageResources({ "test-cert.pem", "test-key.pem", "test.p12" })
void whenPemKeyStoreAndJksTrustStoreProperties() {
Ssl ssl = new Ssl();
ssl.setCertificate("classpath:test-cert.pem");
ssl.setCertificatePrivateKey("classpath:test-key.pem");
ssl.setKeyStoreType("PKCS12");
ssl.setKeyPassword("password");
ssl.setTrustStore("classpath:test.p12");
ssl.setTrustStorePassword("secret");
ssl.setTrustStoreType("PKCS12");
ssl.setClientAuth(Ssl.ClientAuth.NONE);
ssl.setCiphers(new String[] { "ONE", "TWO", "THREE" });
ssl.setEnabledProtocols(new String[] { "TLSv1.1", "TLSv1.2" });
ssl.setProtocol("TLSv1.1");
SslBundle bundle = WebServerSslBundle.get(ssl);
assertThat(bundle).isNotNull();
SslBundleKey key = bundle.getKey();
assertThat(key.getAlias()).isNull();
assertThat(key.getPassword()).isEqualTo("password");
SslStoreBundle stores = bundle.getStores();
assertThat(stores.getKeyStorePassword()).isNull();
assertThat(stores.getKeyStore()).isNotNull();
assertThat(stores.getTrustStore()).isNotNull();
SslOptions options = bundle.getOptions();
assertThat(options.getCiphers()).containsExactly("ONE", "TWO", "THREE");
assertThat(options.getEnabledProtocols()).containsExactly("TLSv1.1", "TLSv1.2");
}
@Test
@WithPackageResources({ "test.p12", "test-cert-chain.pem" })
void whenJksKeyStoreAndPemTrustStoreProperties() {
Ssl ssl = new Ssl();
ssl.setKeyStore("classpath:test.p12");
ssl.setKeyStoreType("PKCS12");
ssl.setKeyPassword("password");
ssl.setTrustCertificate("classpath:test-cert-chain.pem");
ssl.setTrustStorePassword("secret");
ssl.setTrustStoreType("PKCS12");
ssl.setClientAuth(Ssl.ClientAuth.NONE);
ssl.setCiphers(new String[] { "ONE", "TWO", "THREE" });
ssl.setEnabledProtocols(new String[] { "TLSv1.1", "TLSv1.2" });
ssl.setProtocol("TLSv1.1");
SslBundle bundle = WebServerSslBundle.get(ssl);
assertThat(bundle).isNotNull();
SslBundleKey key = bundle.getKey();
assertThat(key.getAlias()).isNull();
assertThat(key.getPassword()).isEqualTo("password");
SslStoreBundle stores = bundle.getStores();
assertThat(stores.getKeyStorePassword()).isNull();
assertThat(stores.getKeyStore()).isNotNull();
assertThat(stores.getTrustStore()).isNotNull();
SslOptions options = bundle.getOptions();
assertThat(options.getCiphers()).containsExactly("ONE", "TWO", "THREE");
assertThat(options.getEnabledProtocols()).containsExactly("TLSv1.1", "TLSv1.2");
}
@Test
void whenMissingPropertiesThrowsException() {
Ssl ssl = new Ssl();
assertThatIllegalStateException().isThrownBy(() -> WebServerSslBundle.get(ssl))
.withMessageContaining("SSL is enabled but no trust material is configured");
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2012-2025 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.web.server.context;
import org.junit.jupiter.api.Test;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.boot.web.server.reactive.ReactiveWebServerFactory;
import org.springframework.boot.web.server.reactive.context.ReactiveWebServerApplicationContext;
import org.springframework.boot.web.server.servlet.ServletWebServerFactory;
import org.springframework.boot.web.server.servlet.context.ServletWebServerApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MissingWebServerFactoryBeanFailureAnalyzer}.
*
* @author Guirong Hu
* @author Andy Wilkinson
*/
class MissingWebServerFactoryBeanFailureAnalyzerTests {
@Test
void missingServletWebServerFactoryBeanFailure() {
ApplicationContextException failure = createFailure(new ServletWebServerApplicationContext());
assertThat(failure).isNotNull();
FailureAnalysis analysis = new MissingWebServerFactoryBeanFailureAnalyzer().analyze(failure);
assertThat(analysis).isNotNull();
assertThat(analysis.getDescription()).isEqualTo("Web application could not be started as there was no "
+ ServletWebServerFactory.class.getName() + " bean defined in the context.");
assertThat(analysis.getAction()).isEqualTo(
"Check your application's dependencies for a supported servlet web server.\nCheck the configured web "
+ "application type.");
}
@Test
void missingReactiveWebServerFactoryBeanFailure() {
ApplicationContextException failure = createFailure(new ReactiveWebServerApplicationContext());
FailureAnalysis analysis = new MissingWebServerFactoryBeanFailureAnalyzer().analyze(failure);
assertThat(analysis).isNotNull();
assertThat(analysis.getDescription()).isEqualTo("Web application could not be started as there was no "
+ ReactiveWebServerFactory.class.getName() + " bean defined in the context.");
assertThat(analysis.getAction()).isEqualTo(
"Check your application's dependencies for a supported reactive web server.\nCheck the configured web "
+ "application type.");
}
private ApplicationContextException createFailure(ConfigurableApplicationContext context) {
try {
context.refresh();
context.close();
return null;
}
catch (ApplicationContextException ex) {
return ex;
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2012-2025 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.web.server.context;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link WebServerApplicationContext}.
*
* @author Phillip Webb
*/
class WebServerApplicationContextTests {
@Test
void hasServerNamespaceWhenContextIsNotWebServerApplicationContextReturnsFalse() {
ApplicationContext context = mock(ApplicationContext.class);
assertThat(WebServerApplicationContext.hasServerNamespace(context, "test")).isFalse();
}
@Test
void hasServerNamespaceWhenContextIsWebServerApplicationContextAndNamespaceDoesNotMatchReturnsFalse() {
ApplicationContext context = mock(WebServerApplicationContext.class);
assertThat(WebServerApplicationContext.hasServerNamespace(context, "test")).isFalse();
}
@Test
void hasServerNamespaceWhenContextIsWebServerApplicationContextAndNamespaceMatchesReturnsTrue() {
WebServerApplicationContext context = mock(WebServerApplicationContext.class);
given(context.getServerNamespace()).willReturn("test");
assertThat(WebServerApplicationContext.hasServerNamespace(context, "test")).isTrue();
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright 2012-2025 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.web.server.context;
import java.io.File;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.boot.web.server.WebServer;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.contentOf;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests {@link WebServerPortFileWriter}.
*
* @author David Liu
* @author Phillip Webb
* @author Andy Wilkinson
*/
class WebServerPortFileWriterTests {
@TempDir
File tempDir;
@BeforeEach
@AfterEach
void reset() {
System.clearProperty("PORTFILE");
}
@Test
void createPortFile() {
File file = new File(this.tempDir, "port.file");
WebServerPortFileWriter listener = new WebServerPortFileWriter(file);
listener.onApplicationEvent(mockEvent("", 8080));
assertThat(contentOf(file)).isEqualTo("8080");
}
@Test
void overridePortFileWithDefault() {
System.setProperty("PORTFILE", new File(this.tempDir, "port.file").getAbsolutePath());
WebServerPortFileWriter listener = new WebServerPortFileWriter();
listener.onApplicationEvent(mockEvent("", 8080));
String content = contentOf(new File(System.getProperty("PORTFILE")));
assertThat(content).isEqualTo("8080");
}
@Test
void overridePortFileWithExplicitFile() {
File file = new File(this.tempDir, "port.file");
System.setProperty("PORTFILE", new File(this.tempDir, "override.file").getAbsolutePath());
WebServerPortFileWriter listener = new WebServerPortFileWriter(file);
listener.onApplicationEvent(mockEvent("", 8080));
String content = contentOf(new File(System.getProperty("PORTFILE")));
assertThat(content).isEqualTo("8080");
}
@Test
void createManagementPortFile() {
File file = new File(this.tempDir, "port.file");
WebServerPortFileWriter listener = new WebServerPortFileWriter(file);
listener.onApplicationEvent(mockEvent("", 8080));
listener.onApplicationEvent(mockEvent("management", 9090));
assertThat(contentOf(file)).isEqualTo("8080");
String managementFile = file.getName();
managementFile = managementFile.substring(0,
managementFile.length() - StringUtils.getFilenameExtension(managementFile).length() - 1);
managementFile = managementFile + "-management." + StringUtils.getFilenameExtension(file.getName());
String content = contentOf(new File(file.getParentFile(), managementFile));
assertThat(content).isEqualTo("9090");
assertThat(collectFileNames(file.getParentFile())).contains(managementFile);
}
@Test
void createUpperCaseManagementPortFile() {
File file = new File(this.tempDir, "port.file");
file = new File(file.getParentFile(), file.getName().toUpperCase(Locale.ENGLISH));
WebServerPortFileWriter listener = new WebServerPortFileWriter(file);
listener.onApplicationEvent(mockEvent("management", 9090));
String managementFile = file.getName();
managementFile = managementFile.substring(0,
managementFile.length() - StringUtils.getFilenameExtension(managementFile).length() - 1);
managementFile = managementFile + "-MANAGEMENT." + StringUtils.getFilenameExtension(file.getName());
String content = contentOf(new File(file.getParentFile(), managementFile));
assertThat(content).isEqualTo("9090");
assertThat(collectFileNames(file.getParentFile())).contains(managementFile);
}
@Test
void getPortFileWhenPortFileNameDoesNotHaveExtension() {
File file = new File(this.tempDir, "portfile");
WebServerPortFileWriter listener = new WebServerPortFileWriter(file);
WebServerApplicationContext applicationContext = mock(WebServerApplicationContext.class);
given(applicationContext.getServerNamespace()).willReturn("management");
assertThat(listener.getPortFile(applicationContext).getName()).isEqualTo("portfile-management");
}
private WebServerInitializedEvent mockEvent(String namespace, int port) {
WebServer webServer = mock(WebServer.class);
given(webServer.getPort()).willReturn(port);
WebServerApplicationContext applicationContext = mock(WebServerApplicationContext.class);
given(applicationContext.getServerNamespace()).willReturn(namespace);
given(applicationContext.getWebServer()).willReturn(webServer);
WebServerInitializedEvent event = mock(WebServerInitializedEvent.class);
given(event.getApplicationContext()).willReturn(applicationContext);
given(event.getWebServer()).willReturn(webServer);
return event;
}
private Set<String> collectFileNames(File directory) {
Set<String> names = new HashSet<>();
if (directory.isDirectory()) {
for (File file : directory.listFiles()) {
names.add(file.getName());
}
}
return names;
}
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright 2012-2025 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.web.server.reactive.context;
import org.junit.jupiter.api.Test;
import org.springframework.boot.web.server.reactive.MockReactiveWebServerFactory;
import org.springframework.boot.web.server.reactive.ReactiveWebServerFactory;
import org.springframework.boot.web.server.reactive.context.WebServerManager.DelayedInitializationHttpHandler;
import org.springframework.boot.web.server.reactive.context.config.ExampleReactiveWebServerApplicationConfiguration;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.http.server.reactive.HttpHandler;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link AnnotationConfigReactiveWebServerApplicationContext}.
*
* @author Phillip Webb
*/
class AnnotationConfigReactiveWebServerApplicationContextTests {
private AnnotationConfigReactiveWebServerApplicationContext context;
@Test
void createFromScan() {
this.context = new AnnotationConfigReactiveWebServerApplicationContext(
ExampleReactiveWebServerApplicationConfiguration.class.getPackage().getName());
verifyContext();
}
@Test
void createFromConfigClass() {
this.context = new AnnotationConfigReactiveWebServerApplicationContext(
ExampleReactiveWebServerApplicationConfiguration.class);
verifyContext();
}
@Test
void registerAndRefresh() {
this.context = new AnnotationConfigReactiveWebServerApplicationContext();
this.context.register(ExampleReactiveWebServerApplicationConfiguration.class);
this.context.refresh();
verifyContext();
}
@Test
void multipleRegistersAndRefresh() {
this.context = new AnnotationConfigReactiveWebServerApplicationContext();
this.context.register(WebServerConfiguration.class);
this.context.register(HttpHandlerConfiguration.class);
this.context.refresh();
assertThat(this.context.getBeansOfType(WebServerConfiguration.class)).hasSize(1);
assertThat(this.context.getBeansOfType(HttpHandlerConfiguration.class)).hasSize(1);
}
@Test
void scanAndRefresh() {
this.context = new AnnotationConfigReactiveWebServerApplicationContext();
this.context.scan(ExampleReactiveWebServerApplicationConfiguration.class.getPackage().getName());
this.context.refresh();
verifyContext();
}
@Test
void httpHandlerInitialization() {
// gh-14666
this.context = new AnnotationConfigReactiveWebServerApplicationContext(InitializationTestConfig.class);
verifyContext();
}
private void verifyContext() {
MockReactiveWebServerFactory factory = this.context.getBean(MockReactiveWebServerFactory.class);
HttpHandler expectedHandler = this.context.getBean(HttpHandler.class);
HttpHandler actualHandler = factory.getWebServer().getHttpHandler();
if (actualHandler instanceof DelayedInitializationHttpHandler delayedHandler) {
actualHandler = delayedHandler.getHandler();
}
assertThat(actualHandler).isEqualTo(expectedHandler);
}
@Configuration(proxyBeanMethods = false)
static class WebServerConfiguration {
@Bean
ReactiveWebServerFactory webServerFactory() {
return new MockReactiveWebServerFactory();
}
}
@Configuration(proxyBeanMethods = false)
static class HttpHandlerConfiguration {
@Bean
HttpHandler httpHandler() {
return mock(HttpHandler.class);
}
}
@Configuration(proxyBeanMethods = false)
static class InitializationTestConfig {
private static boolean addedListener;
@Bean
ReactiveWebServerFactory webServerFactory() {
return new MockReactiveWebServerFactory();
}
@Bean
HttpHandler httpHandler() {
if (!addedListener) {
throw new RuntimeException(
"Handlers should be added after listeners, we're being initialized too early!");
}
return mock(HttpHandler.class);
}
@Bean
Listener listener() {
return new Listener();
}
@Bean
ApplicationEventMulticaster applicationEventMulticaster() {
return new SimpleApplicationEventMulticaster() {
@Override
public void addApplicationListenerBean(String listenerBeanName) {
super.addApplicationListenerBean(listenerBeanName);
if ("listener".equals(listenerBeanName)) {
addedListener = true;
}
}
};
}
static class Listener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
}
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2012-2025 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.web.server.reactive.context;
import org.springframework.boot.AbstractApplicationEnvironmentTests;
import org.springframework.core.env.StandardEnvironment;
/**
* Tests for {@link ApplicationReactiveWebEnvironment}.
*
* @author Phillip Webb
*/
class ApplicationReactiveWebEnvironmentTests extends AbstractApplicationEnvironmentTests {
@Override
protected StandardEnvironment createEnvironment() {
return new ApplicationReactiveWebEnvironment();
}
}

View File

@@ -0,0 +1,265 @@
/*
* Copyright 2012-2025 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.web.server.reactive.context;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.boot.web.server.WebServer;
import org.springframework.boot.web.server.context.ServerPortInfoApplicationContextInitializer;
import org.springframework.boot.web.server.reactive.MockReactiveWebServerFactory;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.http.server.reactive.HttpHandler;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.BDDMockito.then;
import static org.mockito.BDDMockito.willThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
/**
* Tests for {@link ReactiveWebServerApplicationContext}.
*
* @author Andy Wilkinson
*/
class ReactiveWebServerApplicationContextTests {
private final ReactiveWebServerApplicationContext context = new ReactiveWebServerApplicationContext();
@AfterEach
void cleanUp() {
this.context.close();
}
@Test
void whenThereIsNoWebServerFactoryBeanThenContextRefreshWillFail() {
assertThatExceptionOfType(ApplicationContextException.class).isThrownBy(this.context::refresh)
.havingRootCause()
.withMessageContaining(
"Unable to start ReactiveWebServerApplicationContext due to missing ReactiveWebServerFactory bean");
}
@Test
void whenThereIsNoHttpHandlerBeanThenContextRefreshWillFail() {
addWebServerFactoryBean();
assertThatExceptionOfType(ApplicationContextException.class).isThrownBy(this.context::refresh)
.havingRootCause()
.withMessageContaining("Unable to start ReactiveWebApplicationContext due to missing HttpHandler bean");
}
@Test
void whenThereAreMultipleWebServerFactoryBeansThenContextRefreshWillFail() {
addWebServerFactoryBean();
addWebServerFactoryBean("anotherWebServerFactory");
assertThatExceptionOfType(ApplicationContextException.class).isThrownBy(this.context::refresh)
.havingRootCause()
.withMessageContaining(
"Unable to start ReactiveWebApplicationContext due to multiple ReactiveWebServerFactory beans");
}
@Test
void whenThereAreMultipleHttpHandlerBeansThenContextRefreshWillFail() {
addWebServerFactoryBean();
addHttpHandlerBean("httpHandler1");
addHttpHandlerBean("httpHandler2");
assertThatExceptionOfType(ApplicationContextException.class).isThrownBy(this.context::refresh)
.havingRootCause()
.withMessageContaining("Unable to start ReactiveWebApplicationContext due to multiple HttpHandler beans");
}
@Test
void whenContextIsRefreshedThenReactiveWebServerInitializedEventIsPublished() {
addWebServerFactoryBean();
addHttpHandlerBean();
TestApplicationListener listener = new TestApplicationListener();
this.context.addApplicationListener(listener);
this.context.refresh();
List<ApplicationEvent> events = listener.receivedEvents();
assertThat(events).hasSize(2)
.extracting("class")
.containsExactly(ReactiveWebServerInitializedEvent.class, ContextRefreshedEvent.class);
ReactiveWebServerInitializedEvent initializedEvent = (ReactiveWebServerInitializedEvent) events.get(0);
assertThat(initializedEvent.getSource().getPort()).isGreaterThanOrEqualTo(0);
assertThat(initializedEvent.getApplicationContext()).isEqualTo(this.context);
}
@Test
void whenContextIsRefreshedThenLocalServerPortIsAvailableFromTheEnvironment() {
addWebServerFactoryBean();
addHttpHandlerBean();
new ServerPortInfoApplicationContextInitializer().initialize(this.context);
this.context.refresh();
ConfigurableEnvironment environment = this.context.getEnvironment();
assertThat(environment.containsProperty("local.server.port")).isTrue();
assertThat(environment.getProperty("local.server.port")).isEqualTo("8080");
}
@Test
void whenContextRefreshFailedThenWebServerIsStoppedAndDestroyed() {
addWebServerFactoryBean();
addHttpHandlerBean();
this.context.registerBeanDefinition("refreshFailure", new RootBeanDefinition(RefreshFailure.class));
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(this.context::refresh);
WebServer webServer = this.context.getWebServer();
then(webServer).should(times(2)).stop();
then(webServer).should().destroy();
}
@Test
void whenContextRefreshFailedThenWebServerStopFailedCatchStopException() {
addWebServerFactoryBean();
addHttpHandlerBean();
this.context.registerBeanDefinition("refreshFailure", new RootBeanDefinition(RefreshFailure.class, () -> {
willThrow(new RuntimeException("WebServer has failed to stop")).willCallRealMethod()
.given(this.context.getWebServer())
.stop();
return new RefreshFailure();
}));
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(this.context::refresh)
.withStackTraceContaining("WebServer has failed to stop");
WebServer webServer = this.context.getWebServer();
then(webServer).should().stop();
then(webServer).should(never()).destroy();
}
@Test
void whenContextRefreshFailedThenWebServerIsStoppedAndDestroyFailedCatchDestroyException() {
addWebServerFactoryBean();
addHttpHandlerBean();
this.context.registerBeanDefinition("refreshFailure", new RootBeanDefinition(RefreshFailure.class, () -> {
willThrow(new RuntimeException("WebServer has failed to destroy")).willCallRealMethod()
.given(this.context.getWebServer())
.destroy();
return new RefreshFailure();
}));
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(this.context::refresh)
.withStackTraceContaining("WebServer has failed to destroy");
WebServer webServer = this.context.getWebServer();
then(webServer).should().stop();
then(webServer).should().destroy();
}
@Test
void whenContextIsClosedThenWebServerIsStoppedAndDestroyed() {
addWebServerFactoryBean();
addHttpHandlerBean();
this.context.refresh();
MockReactiveWebServerFactory factory = this.context.getBean(MockReactiveWebServerFactory.class);
this.context.close();
then(factory.getWebServer()).should(times(2)).stop();
then(factory.getWebServer()).should().destroy();
}
@Test
@SuppressWarnings("unchecked")
void whenContextIsClosedThenApplicationAvailabilityChangesToRefusingTraffic() {
addWebServerFactoryBean();
addHttpHandlerBean();
TestApplicationListener listener = new TestApplicationListener();
this.context.refresh();
this.context.addApplicationListener(listener);
this.context.close();
List<ApplicationEvent> events = listener.receivedEvents();
assertThat(events).hasSize(2)
.extracting("class")
.contains(AvailabilityChangeEvent.class, ContextClosedEvent.class);
assertThat(((AvailabilityChangeEvent<ReadinessState>) events.get(0)).getState())
.isEqualTo(ReadinessState.REFUSING_TRAFFIC);
}
@Test
void whenContextIsNotActiveThenCloseDoesNotChangeTheApplicationAvailability() {
addWebServerFactoryBean();
addHttpHandlerBean();
TestApplicationListener listener = new TestApplicationListener();
this.context.addApplicationListener(listener);
this.context.registerBeanDefinition("refreshFailure", new RootBeanDefinition(RefreshFailure.class));
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(this.context::refresh);
this.context.close();
assertThat(listener.receivedEvents()).isEmpty();
}
@Test
void whenTheContextIsRefreshedThenASubsequentRefreshAttemptWillFail() {
addWebServerFactoryBean();
addHttpHandlerBean();
this.context.refresh();
assertThatIllegalStateException().isThrownBy(this.context::refresh)
.withMessageContaining("multiple refresh attempts");
}
private void addHttpHandlerBean() {
addHttpHandlerBean("httpHandler");
}
private void addHttpHandlerBean(String beanName) {
this.context.registerBeanDefinition(beanName,
new RootBeanDefinition(HttpHandler.class, () -> (request, response) -> null));
}
private void addWebServerFactoryBean() {
addWebServerFactoryBean("webServerFactory");
}
private void addWebServerFactoryBean(String beanName) {
this.context.registerBeanDefinition(beanName, new RootBeanDefinition(MockReactiveWebServerFactory.class));
}
static class TestApplicationListener implements ApplicationListener<ApplicationEvent> {
private final Deque<ApplicationEvent> events = new ArrayDeque<>();
@Override
public void onApplicationEvent(ApplicationEvent event) {
this.events.add(event);
}
List<ApplicationEvent> receivedEvents() {
List<ApplicationEvent> receivedEvents = new ArrayList<>();
while (!this.events.isEmpty()) {
receivedEvents.add(this.events.pollFirst());
}
return receivedEvents;
}
}
static class RefreshFailure {
RefreshFailure() {
throw new RuntimeException("Fail refresh");
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2012-2025 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.web.server.reactive.context.config;
import org.springframework.boot.web.server.reactive.MockReactiveWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.HttpHandler;
import static org.mockito.Mockito.mock;
/**
* Example {@code @Configuration} for use with
* {@code AnnotationConfigReactiveWebServerApplicationContextTests}.
*
* @author Phillip Webb
*/
@Configuration(proxyBeanMethods = false)
public class ExampleReactiveWebServerApplicationConfiguration {
@Bean
public MockReactiveWebServerFactory webServerFactory() {
return new MockReactiveWebServerFactory();
}
@Bean
public HttpHandler httpHandler() {
return mock(HttpHandler.class);
}
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright 2012-2025 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.web.server.servlet;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import jakarta.servlet.http.Cookie;
import org.junit.jupiter.api.Test;
import org.springframework.boot.web.server.Cookie.SameSite;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.fail;
/**
* Tests for {@link CookieSameSiteSupplier}.
*
* @author Phillip Webb
*/
class CookieSameSiteSupplierTests {
@Test
void whenHasNameWhenNameIsNullThrowsException() {
CookieSameSiteSupplier supplier = (cookie) -> SameSite.LAX;
assertThatIllegalArgumentException().isThrownBy(() -> supplier.whenHasName((String) null))
.withMessage("'name' must not be empty");
}
@Test
void whenHasNameWhenNameIsEmptyThrowsException() {
CookieSameSiteSupplier supplier = (cookie) -> SameSite.LAX;
assertThatIllegalArgumentException().isThrownBy(() -> supplier.whenHasName(""))
.withMessage("'name' must not be empty");
}
@Test
void whenHasNameWhenNameMatchesCallsGetSameSite() {
CookieSameSiteSupplier supplier = (cookie) -> SameSite.LAX;
assertThat(supplier.whenHasName("test").getSameSite(new Cookie("test", "x"))).isEqualTo(SameSite.LAX);
}
@Test
void whenHasNameWhenNameDoesNotMatchDoesNotCallGetSameSite() {
CookieSameSiteSupplier supplier = (cookie) -> fail("Supplier Called");
assertThat(supplier.whenHasName("test").getSameSite(new Cookie("tset", "x"))).isNull();
}
@Test
void whenHasSuppliedNameWhenNameIsNullThrowsException() {
CookieSameSiteSupplier supplier = (cookie) -> SameSite.LAX;
assertThatIllegalArgumentException().isThrownBy(() -> supplier.whenHasName((Supplier<String>) null))
.withMessage("'nameSupplier' must not be null");
}
@Test
void whenHasSuppliedNameWhenNameMatchesCallsGetSameSite() {
CookieSameSiteSupplier supplier = (cookie) -> SameSite.LAX;
assertThat(supplier.whenHasName(() -> "test").getSameSite(new Cookie("test", "x"))).isEqualTo(SameSite.LAX);
}
@Test
void whenHasSuppliedNameWhenNameDoesNotMatchDoesNotCallGetSameSite() {
CookieSameSiteSupplier supplier = (cookie) -> fail("Supplier Called");
assertThat(supplier.whenHasName(() -> "test").getSameSite(new Cookie("tset", "x"))).isNull();
}
@Test
void whenHasNameMatchingRegexWhenRegexIsNullThrowsException() {
CookieSameSiteSupplier supplier = (cookie) -> SameSite.LAX;
assertThatIllegalArgumentException().isThrownBy(() -> supplier.whenHasNameMatching((String) null))
.withMessage("'regex' must not be empty");
}
@Test
void whenHasNameMatchingRegexWhenRegexIsEmptyThrowsException() {
CookieSameSiteSupplier supplier = (cookie) -> SameSite.LAX;
assertThatIllegalArgumentException().isThrownBy(() -> supplier.whenHasNameMatching(""))
.withMessage("'regex' must not be empty");
}
@Test
void whenHasNameMatchingRegexWhenNameMatchesCallsGetSameSite() {
CookieSameSiteSupplier supplier = (cookie) -> SameSite.LAX;
assertThat(supplier.whenHasNameMatching("te.*").getSameSite(new Cookie("test", "x"))).isEqualTo(SameSite.LAX);
}
@Test
void whenHasNameMatchingRegexWhenNameDoesNotMatchDoesNotCallGetSameSite() {
CookieSameSiteSupplier supplier = (cookie) -> fail("Supplier Called");
assertThat(supplier.whenHasNameMatching("te.*").getSameSite(new Cookie("tset", "x"))).isNull();
}
@Test
void whenHasNameMatchingPatternWhenPatternIsNullThrowsException() {
CookieSameSiteSupplier supplier = (cookie) -> SameSite.LAX;
assertThatIllegalArgumentException().isThrownBy(() -> supplier.whenHasNameMatching((Pattern) null))
.withMessage("'pattern' must not be null");
}
@Test
void whenHasNameMatchingPatternWhenNameMatchesCallsGetSameSite() {
CookieSameSiteSupplier supplier = (cookie) -> SameSite.LAX;
assertThat(supplier.whenHasNameMatching(Pattern.compile("te.*")).getSameSite(new Cookie("test", "x")))
.isEqualTo(SameSite.LAX);
}
@Test
void whenHasNameMatchingPatternWhenNameDoesNotMatchDoesNotCallGetSameSite() {
CookieSameSiteSupplier supplier = (cookie) -> fail("Supplier Called");
assertThat(supplier.whenHasNameMatching(Pattern.compile("te.*")).getSameSite(new Cookie("tset", "x"))).isNull();
}
@Test
void whenWhenPredicateIsNullThrowsException() {
CookieSameSiteSupplier supplier = (cookie) -> SameSite.LAX;
assertThatIllegalArgumentException().isThrownBy(() -> supplier.when(null))
.withMessage("'predicate' must not be null");
}
@Test
void whenWhenPredicateMatchesCallsGetSameSite() {
CookieSameSiteSupplier supplier = (cookie) -> SameSite.LAX;
assertThat(supplier.when((cookie) -> cookie.getName().equals("test")).getSameSite(new Cookie("test", "x")))
.isEqualTo(SameSite.LAX);
}
@Test
void whenWhenPredicateDoesNotMatchDoesNotCallGetSameSite() {
CookieSameSiteSupplier supplier = (cookie) -> fail("Supplier Called");
assertThat(supplier.when((cookie) -> cookie.getName().equals("test")).getSameSite(new Cookie("tset", "x")))
.isNull();
}
@Test
void ofNoneSuppliesNone() {
assertThat(CookieSameSiteSupplier.ofNone().getSameSite(new Cookie("test", "x"))).isEqualTo(SameSite.NONE);
}
@Test
void ofLaxSuppliesLax() {
assertThat(CookieSameSiteSupplier.ofLax().getSameSite(new Cookie("test", "x"))).isEqualTo(SameSite.LAX);
}
@Test
void ofStrictSuppliesStrict() {
assertThat(CookieSameSiteSupplier.ofStrict().getSameSite(new Cookie("test", "x"))).isEqualTo(SameSite.STRICT);
}
@Test
void ofWhenNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> CookieSameSiteSupplier.of(null))
.withMessage("'sameSite' must not be null");
}
@Test
void ofSuppliesValue() {
assertThat(CookieSameSiteSupplier.of(SameSite.STRICT).getSameSite(new Cookie("test", "x")))
.isEqualTo(SameSite.STRICT);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2012-2025 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.web.server.servlet;
import java.io.File;
import java.net.URL;
import java.security.CodeSource;
import java.security.cert.Certificate;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DocumentRoot}.
*
* @author Phillip Webb
*/
class DocumentRootTests {
@TempDir
File tempDir;
private final DocumentRoot documentRoot = new DocumentRoot(LogFactory.getLog(getClass()));
@Test
void explodedWarFileDocumentRootWhenRunningFromExplodedWar() throws Exception {
File codeSourceFile = new File(this.tempDir, "test.war/WEB-INF/lib/spring-boot.jar");
codeSourceFile.getParentFile().mkdirs();
codeSourceFile.createNewFile();
File directory = this.documentRoot.getExplodedWarFileDocumentRoot(codeSourceFile);
assertThat(directory).isEqualTo(codeSourceFile.getParentFile().getParentFile().getParentFile());
}
@Test
void explodedWarFileDocumentRootWhenRunningFromPackagedWar() {
File codeSourceFile = new File(this.tempDir, "test.war");
File directory = this.documentRoot.getExplodedWarFileDocumentRoot(codeSourceFile);
assertThat(directory).isNull();
}
@Test
void codeSourceArchivePath() throws Exception {
CodeSource codeSource = new CodeSource(new URL("file", "", "/some/test/path/"), (Certificate[]) null);
File codeSourceArchive = this.documentRoot.getCodeSourceArchive(codeSource);
assertThat(codeSourceArchive).isEqualTo(new File("/some/test/path/"));
}
@Test
void codeSourceArchivePathContainingSpaces() throws Exception {
CodeSource codeSource = new CodeSource(new URL("file", "", "/test/path/with%20space/"), (Certificate[]) null);
File codeSourceArchive = this.documentRoot.getCodeSourceArchive(codeSource);
assertThat(codeSourceArchive).isEqualTo(new File("/test/path/with space/"));
}
}

View File

@@ -0,0 +1,170 @@
/*
* Copyright 2012-2025 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.web.server.servlet;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.List;
import java.util.function.Consumer;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.Assertions.assertThatNoException;
/**
* Tests for {@link StaticResourceJars}.
*
* @author Rupert Madden-Abbott
* @author Andy Wilkinson
*/
class StaticResourceJarsTests {
@TempDir
File tempDir;
@Test
void includeJarWithStaticResources() throws Exception {
File jarFile = createResourcesJar("test-resources.jar");
List<URL> staticResourceJarUrls = new StaticResourceJars().getUrlsFrom(jarFile.toURI().toURL());
assertThat(staticResourceJarUrls).hasSize(1);
}
@Test
void includeJarWithStaticResourcesWithUrlEncodedSpaces() throws Exception {
File jarFile = createResourcesJar("test resources.jar");
List<URL> staticResourceJarUrls = new StaticResourceJars().getUrlsFrom(jarFile.toURI().toURL());
assertThat(staticResourceJarUrls).hasSize(1);
}
@Test
void includeJarWithStaticResourcesWithPlusInItsPath() throws Exception {
File jarFile = createResourcesJar("test + resources.jar");
List<URL> staticResourceJarUrls = new StaticResourceJars().getUrlsFrom(jarFile.toURI().toURL());
assertThat(staticResourceJarUrls).hasSize(1);
}
@Test
void excludeJarWithoutStaticResources() throws Exception {
File jarFile = createJar("dependency.jar");
List<URL> staticResourceJarUrls = new StaticResourceJars().getUrlsFrom(jarFile.toURI().toURL());
assertThat(staticResourceJarUrls).isEmpty();
}
@Test
void uncPathsAreTolerated() throws Exception {
File jarFile = createResourcesJar("test-resources.jar");
List<URL> staticResourceJarUrls = new StaticResourceJars().getUrlsFrom(jarFile.toURI().toURL(),
new URL("file://unc.example.com/test.jar"));
assertThat(staticResourceJarUrls).hasSize(1);
}
@Test
void ignoreWildcardUrls() throws Exception {
File jarFile = createResourcesJar("test-resources.jar");
URL folderUrl = jarFile.getParentFile().toURI().toURL();
URL wildcardUrl = new URL(folderUrl + "*.jar");
List<URL> staticResourceJarUrls = new StaticResourceJars().getUrlsFrom(wildcardUrl);
assertThat(staticResourceJarUrls).isEmpty();
}
@Test
void doesNotCloseJarFromCachedConnection() throws Exception {
File jarFile = createResourcesJar("test-resources.jar");
TrackedURLStreamHandler handler = new TrackedURLStreamHandler(true);
URL url = new URL("jar", null, 0, jarFile.toURI().toURL() + "!/", handler);
try {
new StaticResourceJars().getUrlsFrom(url);
assertThatNoException()
.isThrownBy(() -> ((JarURLConnection) handler.getConnection()).getJarFile().getComment());
}
finally {
((JarURLConnection) handler.getConnection()).getJarFile().close();
}
}
@Test
void closesJarFromNonCachedConnection() throws Exception {
File jarFile = createResourcesJar("test-resources.jar");
TrackedURLStreamHandler handler = new TrackedURLStreamHandler(false);
URL url = new URL("jar", null, 0, jarFile.toURI().toURL() + "!/", handler);
new StaticResourceJars().getUrlsFrom(url);
assertThatIllegalStateException()
.isThrownBy(() -> ((JarURLConnection) handler.getConnection()).getJarFile().getComment())
.withMessageContaining("closed");
}
private File createResourcesJar(String name) throws IOException {
return createJar(name, (output) -> {
JarEntry jarEntry = new JarEntry("META-INF/resources");
try {
output.putNextEntry(jarEntry);
output.closeEntry();
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
});
}
private File createJar(String name) throws IOException {
return createJar(name, null);
}
private File createJar(String name, Consumer<JarOutputStream> customizer) throws IOException {
File jarFile = new File(this.tempDir, name);
JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(jarFile));
if (customizer != null) {
customizer.accept(jarOutputStream);
}
jarOutputStream.close();
return jarFile;
}
private static class TrackedURLStreamHandler extends URLStreamHandler {
private final boolean useCaches;
private URLConnection connection;
TrackedURLStreamHandler(boolean useCaches) {
this.useCaches = useCaches;
}
@Override
protected URLConnection openConnection(URL u) throws IOException {
this.connection = new URL(u.toExternalForm()).openConnection();
this.connection.setUseCaches(this.useCaches);
return this.connection;
}
URLConnection getConnection() {
return this.connection;
}
}
}

View File

@@ -0,0 +1,222 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context;
import jakarta.servlet.GenericServlet;
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.server.servlet.MockServletWebServerFactory;
import org.springframework.boot.web.server.servlet.ServletWebServerFactory;
import org.springframework.boot.web.server.servlet.context.config.ExampleServletWebServerApplicationConfiguration;
import org.springframework.boot.web.servlet.mock.MockServlet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.then;
/**
* Tests for {@link AnnotationConfigServletWebServerApplicationContext}.
*
* @author Phillip Webb
*/
class AnnotationConfigServletWebServerApplicationContextTests {
private AnnotationConfigServletWebServerApplicationContext context;
@AfterEach
void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
void createFromScan() {
this.context = new AnnotationConfigServletWebServerApplicationContext(
ExampleServletWebServerApplicationConfiguration.class.getPackage().getName());
verifyContext();
}
@Test
void sessionScopeAvailable() {
this.context = new AnnotationConfigServletWebServerApplicationContext(
ExampleServletWebServerApplicationConfiguration.class, SessionScopedComponent.class);
verifyContext();
}
@Test
void sessionScopeAvailableToServlet() {
this.context = new AnnotationConfigServletWebServerApplicationContext(
ExampleServletWebServerApplicationConfiguration.class, ExampleServletWithAutowired.class,
SessionScopedComponent.class);
Servlet servlet = this.context.getBean(ExampleServletWithAutowired.class);
assertThat(servlet).isNotNull();
}
@Test
void createFromConfigClass() {
this.context = new AnnotationConfigServletWebServerApplicationContext(
ExampleServletWebServerApplicationConfiguration.class);
verifyContext();
}
@Test
void registerAndRefresh() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(ExampleServletWebServerApplicationConfiguration.class);
this.context.refresh();
verifyContext();
}
@Test
void multipleRegistersAndRefresh() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(WebServerConfiguration.class);
this.context.register(ServletContextAwareConfiguration.class);
this.context.refresh();
assertThat(this.context.getBeansOfType(Servlet.class)).hasSize(1);
assertThat(this.context.getBeansOfType(ServletWebServerFactory.class)).hasSize(1);
}
@Test
void scanAndRefresh() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.scan(ExampleServletWebServerApplicationConfiguration.class.getPackage().getName());
this.context.refresh();
verifyContext();
}
@Test
void createAndInitializeCyclic() {
this.context = new AnnotationConfigServletWebServerApplicationContext(
ServletContextAwareEmbeddedConfiguration.class);
verifyContext();
// You can't initialize the application context and inject the servlet context
// because of a cycle - we'd like this to be not null, but it never will be
assertThat(this.context.getBean(ServletContextAwareEmbeddedConfiguration.class).getServletContext()).isNull();
}
@Test
void createAndInitializeWithParent() {
AnnotationConfigServletWebServerApplicationContext parent = new AnnotationConfigServletWebServerApplicationContext(
WebServerConfiguration.class);
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(WebServerConfiguration.class, ServletContextAwareConfiguration.class);
this.context.setParent(parent);
this.context.refresh();
verifyContext();
assertThat(this.context.getBean(ServletContextAwareConfiguration.class).getServletContext()).isNotNull();
}
private void verifyContext() {
MockServletWebServerFactory factory = this.context.getBean(MockServletWebServerFactory.class);
Servlet servlet = this.context.getBean(Servlet.class);
then(factory.getServletContext()).should().addServlet("servlet", servlet);
}
@Component
static class ExampleServletWithAutowired extends GenericServlet {
@Autowired
private SessionScopedComponent component;
@Override
public void service(ServletRequest req, ServletResponse res) {
assertThat(this.component).isNotNull();
}
}
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
static class SessionScopedComponent {
}
@Configuration(proxyBeanMethods = false)
@EnableWebMvc
static class ServletContextAwareEmbeddedConfiguration implements ServletContextAware {
private ServletContext servletContext;
@Bean
ServletWebServerFactory webServerFactory() {
return new MockServletWebServerFactory();
}
@Bean
Servlet servlet() {
return new MockServlet();
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
ServletContext getServletContext() {
return this.servletContext;
}
}
@Configuration(proxyBeanMethods = false)
static class WebServerConfiguration {
@Bean
ServletWebServerFactory webServerFactory() {
return new MockServletWebServerFactory();
}
}
@Configuration(proxyBeanMethods = false)
@EnableWebMvc
static class ServletContextAwareConfiguration implements ServletContextAware {
private ServletContext servletContext;
@Bean
Servlet servlet() {
return new MockServlet();
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
ServletContext getServletContext() {
return this.servletContext;
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context;
import org.springframework.boot.AbstractApplicationEnvironmentTests;
import org.springframework.boot.web.context.servlet.ApplicationServletEnvironment;
import org.springframework.core.env.StandardEnvironment;
/**
* Tests for {@link ApplicationServletEnvironment}.
*
* @author Phillip Webb
*/
class ApplicationServletEnvironmentTests extends AbstractApplicationEnvironmentTests {
@Override
protected StandardEnvironment createEnvironment() {
return new ApplicationServletEnvironment();
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Map;
import java.util.Properties;
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.annotation.WebListener;
import jakarta.servlet.annotation.WebServlet;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.boot.testsupport.classpath.ForkedClassPath;
import org.springframework.boot.web.context.servlet.AnnotationConfigServletWebApplicationContext;
import org.springframework.boot.web.server.servlet.WebListenerRegistrar;
import org.springframework.boot.web.server.servlet.WebListenerRegistry;
import org.springframework.boot.web.server.servlet.context.testcomponents.filter.TestFilter;
import org.springframework.boot.web.server.servlet.context.testcomponents.listener.TestListener;
import org.springframework.boot.web.server.servlet.context.testcomponents.servlet.TestMultipartServlet;
import org.springframework.boot.web.server.servlet.context.testcomponents.servlet.TestServlet;
import org.springframework.boot.web.servlet.RegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.mock.web.MockServletContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
/**
* Integration tests for {@link ServletComponentScan @ServletComponentScan} with a mock
* web environment.
*
* @author Andy Wilkinson
*/
class MockWebEnvironmentServletComponentScanIntegrationTests {
private AnnotationConfigServletWebApplicationContext context;
@TempDir
File temp;
@AfterEach
void cleanUp() {
if (this.context != null) {
this.context.close();
}
}
@Test
@ForkedClassPath
void componentsAreRegistered() {
prepareContext();
this.context.refresh();
Map<String, RegistrationBean> registrationBeans = this.context.getBeansOfType(RegistrationBean.class);
assertThat(registrationBeans).hasSize(3);
assertThat(registrationBeans.keySet()).containsExactlyInAnyOrder(TestServlet.class.getName(),
TestFilter.class.getName(), TestMultipartServlet.class.getName());
WebListenerRegistry registry = mock(WebListenerRegistry.class);
this.context.getBean(WebListenerRegistrar.class).register(registry);
then(registry).should().addWebListeners(TestListener.class.getName());
}
@Test
@ForkedClassPath
void indexedComponentsAreRegistered() throws IOException {
writeIndex(this.temp);
prepareContext();
try (URLClassLoader classLoader = new URLClassLoader(new URL[] { this.temp.toURI().toURL() },
getClass().getClassLoader())) {
this.context.setClassLoader(classLoader);
this.context.refresh();
Map<String, RegistrationBean> registrationBeans = this.context.getBeansOfType(RegistrationBean.class);
assertThat(registrationBeans).hasSize(2);
assertThat(registrationBeans.keySet()).containsExactlyInAnyOrder(TestServlet.class.getName(),
TestFilter.class.getName());
WebListenerRegistry registry = mock(WebListenerRegistry.class);
this.context.getBean(WebListenerRegistrar.class).register(registry);
then(registry).should().addWebListeners(TestListener.class.getName());
}
}
@Test
@ForkedClassPath
void multipartConfigIsHonoured() {
prepareContext();
this.context.refresh();
@SuppressWarnings("rawtypes")
Map<String, ServletRegistrationBean> beans = this.context.getBeansOfType(ServletRegistrationBean.class);
ServletRegistrationBean<?> servletRegistrationBean = beans.get(TestMultipartServlet.class.getName());
assertThat(servletRegistrationBean).isNotNull();
MultipartConfigElement multipartConfig = servletRegistrationBean.getMultipartConfig();
assertThat(multipartConfig).isNotNull();
assertThat(multipartConfig.getLocation()).isEqualTo("test");
assertThat(multipartConfig.getMaxRequestSize()).isEqualTo(2048);
assertThat(multipartConfig.getMaxFileSize()).isEqualTo(1024);
assertThat(multipartConfig.getFileSizeThreshold()).isEqualTo(512);
}
private void writeIndex(File temp) throws IOException {
File metaInf = new File(temp, "META-INF");
metaInf.mkdirs();
Properties index = new Properties();
index.setProperty(TestFilter.class.getName(), WebFilter.class.getName());
index.setProperty(TestListener.class.getName(), WebListener.class.getName());
index.setProperty(TestServlet.class.getName(), WebServlet.class.getName());
try (FileWriter writer = new FileWriter(new File(metaInf, "spring.components"))) {
index.store(writer, null);
}
}
private void prepareContext() {
this.context = new AnnotationConfigServletWebApplicationContext();
this.context.register(ScanningConfiguration.class);
this.context.setServletContext(new MockServletContext());
}
@ServletComponentScan(basePackages = "org.springframework.boot.web.server.servlet.context.testcomponents")
static class ScanningConfiguration {
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Map;
import java.util.Properties;
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.annotation.WebListener;
import jakarta.servlet.annotation.WebServlet;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.web.server.servlet.ConfigurableServletWebServerFactory;
import org.springframework.boot.web.server.servlet.MockServletWebServerFactory;
import org.springframework.boot.web.server.servlet.ServletWebServerFactory;
import org.springframework.boot.web.server.servlet.WebListenerRegistrar;
import org.springframework.boot.web.server.servlet.context.testcomponents.filter.TestFilter;
import org.springframework.boot.web.server.servlet.context.testcomponents.listener.TestListener;
import org.springframework.boot.web.server.servlet.context.testcomponents.servlet.TestMultipartServlet;
import org.springframework.boot.web.server.servlet.context.testcomponents.servlet.TestServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link ServletComponentScan @ServletComponentScan}
*
* @author Andy Wilkinson
*/
class ServletComponentScanIntegrationTests {
private AnnotationConfigServletWebServerApplicationContext context;
@TempDir
File temp;
@AfterEach
void cleanUp() {
if (this.context != null) {
this.context.close();
}
}
@Test
void componentsAreRegistered() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(TestConfiguration.class);
this.context.refresh();
assertThat(this.context.getServletContext().getFilterRegistrations()).hasSize(1)
.containsKey(TestFilter.class.getName());
assertThat(this.context.getServletContext().getServletRegistrations()).hasSize(2)
.containsKeys(TestServlet.class.getName(), TestMultipartServlet.class.getName());
assertThat(this.context.getBean(MockServletWebServerFactory.class).getSettings().getWebListenerClassNames())
.containsExactly(TestListener.class.getName());
}
@Test
void indexedComponentsAreRegistered() throws IOException {
writeIndex(this.temp);
this.context = new AnnotationConfigServletWebServerApplicationContext();
try (URLClassLoader classLoader = new URLClassLoader(new URL[] { this.temp.toURI().toURL() },
getClass().getClassLoader())) {
this.context.setClassLoader(classLoader);
this.context.register(TestConfiguration.class);
this.context.refresh();
assertThat(this.context.getServletContext().getFilterRegistrations()).hasSize(1)
.containsKey(TestFilter.class.getName());
assertThat(this.context.getServletContext().getServletRegistrations()).hasSize(1)
.containsKeys(TestServlet.class.getName());
assertThat(this.context.getBean(MockServletWebServerFactory.class).getSettings().getWebListenerClassNames())
.containsExactly(TestListener.class.getName());
}
}
@Test
void multipartConfigIsHonoured() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(TestConfiguration.class);
this.context.refresh();
@SuppressWarnings("rawtypes")
Map<String, ServletRegistrationBean> beans = this.context.getBeansOfType(ServletRegistrationBean.class);
ServletRegistrationBean<?> servletRegistrationBean = beans.get(TestMultipartServlet.class.getName());
assertThat(servletRegistrationBean).isNotNull();
MultipartConfigElement multipartConfig = servletRegistrationBean.getMultipartConfig();
assertThat(multipartConfig).isNotNull();
assertThat(multipartConfig.getLocation()).isEqualTo("test");
assertThat(multipartConfig.getMaxRequestSize()).isEqualTo(2048);
assertThat(multipartConfig.getMaxFileSize()).isEqualTo(1024);
assertThat(multipartConfig.getFileSizeThreshold()).isEqualTo(512);
}
private void writeIndex(File temp) throws IOException {
File metaInf = new File(temp, "META-INF");
metaInf.mkdirs();
Properties index = new Properties();
index.setProperty(TestFilter.class.getName(), WebFilter.class.getName());
index.setProperty(TestListener.class.getName(), WebListener.class.getName());
index.setProperty(TestServlet.class.getName(), WebServlet.class.getName());
try (FileWriter writer = new FileWriter(new File(metaInf, "spring.components"))) {
index.store(writer, null);
}
}
@ServletComponentScan(basePackages = "org.springframework.boot.web.server.servlet.context.testcomponents")
static class TestConfiguration {
@Bean
protected ServletWebServerFactory webServerFactory(ObjectProvider<WebListenerRegistrar> webListenerRegistrars) {
ConfigurableServletWebServerFactory factory = new MockServletWebServerFactory();
webListenerRegistrars.orderedStream().forEach((registrar) -> registrar.register(factory));
return factory;
}
}
}

View File

@@ -0,0 +1,235 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context;
import java.util.function.Consumer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.web.server.servlet.context.testcomponents.listener.TestListener;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.aot.ApplicationContextAotGenerator;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
import org.springframework.core.test.tools.TestCompiler;
import org.springframework.javapoet.ClassName;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatNoException;
/**
* Tests for {@link ServletComponentScanRegistrar}
*
* @author Andy Wilkinson
*/
class ServletComponentScanRegistrarTests {
private AnnotationConfigApplicationContext context;
@AfterEach
void after() {
if (this.context != null) {
this.context.close();
}
}
@Test
void packagesConfiguredWithValue() {
this.context = new AnnotationConfigApplicationContext(ValuePackages.class);
ServletComponentRegisteringPostProcessor postProcessor = this.context
.getBean(ServletComponentRegisteringPostProcessor.class);
assertThat(postProcessor.getPackagesToScan()).contains("com.example.foo", "com.example.bar");
}
@Test
void packagesConfiguredWithValueAsm() {
this.context = new AnnotationConfigApplicationContext();
this.context.registerBeanDefinition("valuePackages", new RootBeanDefinition(ValuePackages.class.getName()));
this.context.refresh();
ServletComponentRegisteringPostProcessor postProcessor = this.context
.getBean(ServletComponentRegisteringPostProcessor.class);
assertThat(postProcessor.getPackagesToScan()).contains("com.example.foo", "com.example.bar");
}
@Test
void packagesConfiguredWithBackPackages() {
this.context = new AnnotationConfigApplicationContext(BasePackages.class);
ServletComponentRegisteringPostProcessor postProcessor = this.context
.getBean(ServletComponentRegisteringPostProcessor.class);
assertThat(postProcessor.getPackagesToScan()).contains("com.example.foo", "com.example.bar");
}
@Test
void packagesConfiguredWithBasePackageClasses() {
this.context = new AnnotationConfigApplicationContext(BasePackageClasses.class);
ServletComponentRegisteringPostProcessor postProcessor = this.context
.getBean(ServletComponentRegisteringPostProcessor.class);
assertThat(postProcessor.getPackagesToScan()).contains(getClass().getPackage().getName());
}
@Test
void packagesConfiguredWithBothValueAndBasePackages() {
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> this.context = new AnnotationConfigApplicationContext(ValueAndBasePackages.class))
.withMessageContaining("'value'")
.withMessageContaining("'basePackages'")
.withMessageContaining("com.example.foo")
.withMessageContaining("com.example.bar");
}
@Test
void packagesFromMultipleAnnotationsAreMerged() {
this.context = new AnnotationConfigApplicationContext(BasePackages.class, AdditionalPackages.class);
ServletComponentRegisteringPostProcessor postProcessor = this.context
.getBean(ServletComponentRegisteringPostProcessor.class);
assertThat(postProcessor.getPackagesToScan()).contains("com.example.foo", "com.example.bar", "com.example.baz");
}
@Test
void withNoBasePackagesScanningUsesBasePackageOfAnnotatedClass() {
this.context = new AnnotationConfigApplicationContext(NoBasePackages.class);
ServletComponentRegisteringPostProcessor postProcessor = this.context
.getBean(ServletComponentRegisteringPostProcessor.class);
assertThat(postProcessor.getPackagesToScan())
.containsExactly("org.springframework.boot.web.server.servlet.context");
}
@Test
void noBasePackageAndBasePackageAreCombinedCorrectly() {
this.context = new AnnotationConfigApplicationContext(NoBasePackages.class, BasePackages.class);
ServletComponentRegisteringPostProcessor postProcessor = this.context
.getBean(ServletComponentRegisteringPostProcessor.class);
assertThat(postProcessor.getPackagesToScan()).containsExactlyInAnyOrder(
"org.springframework.boot.web.server.servlet.context", "com.example.foo", "com.example.bar");
}
@Test
void basePackageAndNoBasePackageAreCombinedCorrectly() {
this.context = new AnnotationConfigApplicationContext(BasePackages.class, NoBasePackages.class);
ServletComponentRegisteringPostProcessor postProcessor = this.context
.getBean(ServletComponentRegisteringPostProcessor.class);
assertThat(postProcessor.getPackagesToScan()).containsExactlyInAnyOrder(
"org.springframework.boot.web.server.servlet.context", "com.example.foo", "com.example.bar");
}
@Test
@CompileWithForkedClassLoader
void processAheadOfTimeDoesNotRegisterServletComponentRegisteringPostProcessor() {
GenericApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean(BasePackages.class);
compile(context, (freshContext) -> {
freshContext.refresh();
assertThat(freshContext.getBeansOfType(ServletComponentRegisteringPostProcessor.class)).isEmpty();
});
}
@Test
void processAheadOfTimeRegistersReflectionHintsForWebListeners() {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext();
context.registerBean(ScanListenerPackage.class);
TestGenerationContext generationContext = new TestGenerationContext(
ClassName.get(getClass().getPackageName(), "TestTarget"));
new ApplicationContextAotGenerator().processAheadOfTime(context, generationContext);
assertThat(RuntimeHintsPredicates.reflection()
.onType(TestListener.class)
.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
.accepts(generationContext.getRuntimeHints());
}
@Test
void processAheadOfTimeSucceedsForWebServletWithMultipartConfig() {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext();
context.registerBean(ScanServletPackage.class);
TestGenerationContext generationContext = new TestGenerationContext(
ClassName.get(getClass().getPackageName(), "TestTarget"));
assertThatNoException()
.isThrownBy(() -> new ApplicationContextAotGenerator().processAheadOfTime(context, generationContext));
}
@SuppressWarnings("unchecked")
private void compile(GenericApplicationContext context, Consumer<GenericApplicationContext> freshContext) {
TestGenerationContext generationContext = new TestGenerationContext(
ClassName.get(getClass().getPackageName(), "TestTarget"));
ClassName className = new ApplicationContextAotGenerator().processAheadOfTime(context, generationContext);
generationContext.writeGeneratedContent();
TestCompiler.forSystem().with(generationContext).compile((compiled) -> {
GenericApplicationContext freshApplicationContext = new GenericApplicationContext();
ApplicationContextInitializer<GenericApplicationContext> initializer = compiled
.getInstance(ApplicationContextInitializer.class, className.toString());
initializer.initialize(freshApplicationContext);
freshContext.accept(freshApplicationContext);
});
}
@Configuration(proxyBeanMethods = false)
@ServletComponentScan({ "com.example.foo", "com.example.bar" })
static class ValuePackages {
}
@Configuration(proxyBeanMethods = false)
@ServletComponentScan(basePackages = { "com.example.foo", "com.example.bar" })
static class BasePackages {
}
@Configuration(proxyBeanMethods = false)
@ServletComponentScan(basePackages = "com.example.baz")
static class AdditionalPackages {
}
@Configuration(proxyBeanMethods = false)
@ServletComponentScan(basePackageClasses = ServletComponentScanRegistrarTests.class)
static class BasePackageClasses {
}
@Configuration(proxyBeanMethods = false)
@ServletComponentScan(value = "com.example.foo", basePackages = "com.example.bar")
static class ValueAndBasePackages {
}
@Configuration(proxyBeanMethods = false)
@ServletComponentScan
static class NoBasePackages {
}
@Configuration(proxyBeanMethods = false)
@ServletComponentScan("org.springframework.boot.web.server.servlet.context.testcomponents.listener")
static class ScanListenerPackage {
}
@Configuration(proxyBeanMethods = false)
@ServletComponentScan("org.springframework.boot.web.server.servlet.context.testcomponents.servlet")
static class ScanServletPackage {
}
}

View File

@@ -0,0 +1,619 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.EnumSet;
import java.util.List;
import java.util.Properties;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanDefinition;
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.AbstractBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.testsupport.system.CapturedOutput;
import org.springframework.boot.testsupport.system.OutputCaptureExtension;
import org.springframework.boot.web.server.WebServer;
import org.springframework.boot.web.server.context.ServerPortInfoApplicationContextInitializer;
import org.springframework.boot.web.server.servlet.MockServletWebServerFactory;
import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.SessionScope;
import org.springframework.web.filter.GenericFilterBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.BDDMockito.willThrow;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.withSettings;
/**
* Tests for {@link ServletWebServerApplicationContext}.
*
* @author Phillip Webb
* @author Stephane Nicoll
*/
@ExtendWith({ OutputCaptureExtension.class, MockitoExtension.class })
class ServletWebServerApplicationContextTests {
private final ServletWebServerApplicationContext context = new ServletWebServerApplicationContext();
@Captor
private ArgumentCaptor<Filter> filterCaptor;
@AfterEach
void cleanup() {
this.context.close();
}
@Test
void startRegistrations() {
addWebServerFactoryBean();
this.context.refresh();
MockServletWebServerFactory factory = getWebServerFactory();
// Ensure that the context has been set up
assertThat(this.context.getServletContext()).isEqualTo(factory.getServletContext());
then(factory.getServletContext()).should()
.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
// Ensure WebApplicationContextUtils.registerWebApplicationScopes was called
assertThat(this.context.getBeanFactory().getRegisteredScope(WebApplicationContext.SCOPE_SESSION))
.isInstanceOf(SessionScope.class);
// Ensure WebApplicationContextUtils.registerEnvironmentBeans was called
assertThat(this.context.containsBean(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME)).isTrue();
}
@Test
void doesNotRegistersShutdownHook() {
// See gh-314 for background. We no longer register the shutdown hook
// since it is really the caller's responsibility. The shutdown hook could
// also be problematic in a classic WAR deployment.
addWebServerFactoryBean();
this.context.refresh();
assertThat(this.context).hasFieldOrPropertyWithValue("shutdownHook", null);
}
@Test
void ServletWebServerInitializedEventPublished() {
addWebServerFactoryBean();
this.context.registerBeanDefinition("listener", new RootBeanDefinition(TestApplicationListener.class));
this.context.refresh();
List<ApplicationEvent> events = this.context.getBean(TestApplicationListener.class).receivedEvents();
assertThat(events).hasSize(2)
.extracting("class")
.containsExactly(ServletWebServerInitializedEvent.class, ContextRefreshedEvent.class);
ServletWebServerInitializedEvent initializedEvent = (ServletWebServerInitializedEvent) events.get(0);
assertThat(initializedEvent.getSource().getPort()).isGreaterThanOrEqualTo(0);
assertThat(initializedEvent.getApplicationContext()).isEqualTo(this.context);
}
@Test
void localPortIsAvailable() {
addWebServerFactoryBean();
new ServerPortInfoApplicationContextInitializer().initialize(this.context);
this.context.refresh();
ConfigurableEnvironment environment = this.context.getEnvironment();
assertThat(environment.containsProperty("local.server.port")).isTrue();
assertThat(environment.getProperty("local.server.port")).isEqualTo("8080");
}
@Test
void stopOnStop() {
addWebServerFactoryBean();
this.context.refresh();
MockServletWebServerFactory factory = getWebServerFactory();
then(factory.getWebServer()).should().start();
this.context.stop();
then(factory.getWebServer()).should().stop();
}
@Test
void startOnStartAfterStop() {
addWebServerFactoryBean();
this.context.refresh();
MockServletWebServerFactory factory = getWebServerFactory();
then(factory.getWebServer()).should().start();
this.context.stop();
then(factory.getWebServer()).should().stop();
this.context.start();
then(factory.getWebServer()).should(times(2)).start();
}
@Test
void stopAndDestroyOnClose() {
addWebServerFactoryBean();
this.context.refresh();
MockServletWebServerFactory factory = getWebServerFactory();
this.context.close();
then(factory.getWebServer()).should(times(2)).stop();
then(factory.getWebServer()).should().destroy();
}
@Test
void applicationIsUnreadyDuringShutdown() {
TestApplicationListener listener = new TestApplicationListener();
addWebServerFactoryBean();
this.context.refresh();
this.context.addApplicationListener(listener);
this.context.close();
assertThat(listener.receivedEvents()).hasSize(2)
.extracting("class")
.contains(AvailabilityChangeEvent.class, ContextClosedEvent.class);
}
@Test
void whenContextIsNotActiveThenCloseDoesNotChangeTheApplicationAvailability() {
addWebServerFactoryBean();
TestApplicationListener listener = new TestApplicationListener();
this.context.addApplicationListener(listener);
this.context.registerBeanDefinition("refreshFailure", new RootBeanDefinition(RefreshFailure.class));
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(this.context::refresh);
this.context.close();
assertThat(listener.receivedEvents()).isEmpty();
}
@Test
void whenContextRefreshFailedThenWebServerIsStoppedAndDestroyed() {
addWebServerFactoryBean();
this.context.registerBeanDefinition("refreshFailure", new RootBeanDefinition(RefreshFailure.class));
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(this.context::refresh);
WebServer webServer = this.context.getWebServer();
then(webServer).should(times(2)).stop();
then(webServer).should().destroy();
}
@Test
void whenContextRefreshFailedThenWebServerStopFailedCatchStopException() {
addWebServerFactoryBean();
this.context.registerBeanDefinition("refreshFailure", new RootBeanDefinition(RefreshFailure.class, () -> {
willThrow(new RuntimeException("WebServer has failed to stop")).willCallRealMethod()
.given(this.context.getWebServer())
.stop();
return new RefreshFailure();
}));
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(this.context::refresh)
.withStackTraceContaining("WebServer has failed to stop");
WebServer webServer = this.context.getWebServer();
then(webServer).should().stop();
then(webServer).should(never()).destroy();
}
@Test
void whenContextRefreshFailedThenWebServerIsStoppedAndDestroyFailedCatchDestroyException() {
addWebServerFactoryBean();
this.context.registerBeanDefinition("refreshFailure", new RootBeanDefinition(RefreshFailure.class, () -> {
willThrow(new RuntimeException("WebServer has failed to destroy")).willCallRealMethod()
.given(this.context.getWebServer())
.destroy();
return new RefreshFailure();
}));
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(this.context::refresh)
.withStackTraceContaining("WebServer has failed to destroy");
WebServer webServer = this.context.getWebServer();
then(webServer).should().stop();
then(webServer).should().destroy();
}
@Test
void cannotSecondRefresh() {
addWebServerFactoryBean();
this.context.refresh();
assertThatIllegalStateException().isThrownBy(this.context::refresh);
}
@Test
void servletContextAwareBeansAreInjected() {
addWebServerFactoryBean();
ServletContextAware bean = mock(ServletContextAware.class);
this.context.registerBeanDefinition("bean", beanDefinition(bean));
this.context.refresh();
then(bean).should().setServletContext(getWebServerFactory().getServletContext());
}
@Test
void missingServletWebServerFactory() {
assertThatExceptionOfType(ApplicationContextException.class).isThrownBy(this.context::refresh)
.havingRootCause()
.withMessageContaining("Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean");
}
@Test
void tooManyWebServerFactories() {
addWebServerFactoryBean();
this.context.registerBeanDefinition("webServerFactory2",
new RootBeanDefinition(MockServletWebServerFactory.class));
assertThatExceptionOfType(ApplicationContextException.class).isThrownBy(this.context::refresh)
.havingRootCause()
.withMessageContaining("Unable to start ServletWebServerApplicationContext due to "
+ "multiple ServletWebServerFactory beans");
}
@Test
void singleServletBean() {
addWebServerFactoryBean();
Servlet servlet = mock(Servlet.class);
this.context.registerBeanDefinition("servletBean", beanDefinition(servlet));
this.context.refresh();
MockServletWebServerFactory factory = getWebServerFactory();
then(factory.getServletContext()).should().addServlet("servletBean", servlet);
then(factory.getRegisteredServlet(0).getRegistration()).should().addMapping("/");
}
@Test
void orderedBeanInsertedCorrectly() {
addWebServerFactoryBean();
OrderedFilter filter = new OrderedFilter();
this.context.registerBeanDefinition("filterBean", beanDefinition(filter));
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
registration.setName("filterBeanRegistration");
registration.setFilter(mock(Filter.class));
registration.setOrder(100);
this.context.registerBeanDefinition("filterRegistrationBean", beanDefinition(registration));
this.context.refresh();
MockServletWebServerFactory factory = getWebServerFactory();
then(factory.getServletContext()).should().addFilter("filterBean", filter);
then(factory.getServletContext()).should().addFilter("filterBeanRegistration", registration.getFilter());
assertThat(factory.getRegisteredFilter(0).getFilter()).isEqualTo(filter);
}
@Test
void multipleServletBeans() {
addWebServerFactoryBean();
Servlet servlet1 = mock(Servlet.class, withSettings().extraInterfaces(Ordered.class));
given(((Ordered) servlet1).getOrder()).willReturn(1);
Servlet servlet2 = mock(Servlet.class, withSettings().extraInterfaces(Ordered.class));
given(((Ordered) servlet2).getOrder()).willReturn(2);
this.context.registerBeanDefinition("servletBean2", beanDefinition(servlet2));
this.context.registerBeanDefinition("servletBean1", beanDefinition(servlet1));
this.context.refresh();
MockServletWebServerFactory factory = getWebServerFactory();
ServletContext servletContext = factory.getServletContext();
InOrder ordered = inOrder(servletContext);
then(servletContext).should(ordered).addServlet("servletBean1", servlet1);
then(servletContext).should(ordered).addServlet("servletBean2", servlet2);
then(factory.getRegisteredServlet(0).getRegistration()).should().addMapping("/servletBean1/");
then(factory.getRegisteredServlet(1).getRegistration()).should().addMapping("/servletBean2/");
}
@Test
void multipleServletBeansWithMainDispatcher() {
addWebServerFactoryBean();
Servlet servlet1 = mock(Servlet.class, withSettings().extraInterfaces(Ordered.class));
given(((Ordered) servlet1).getOrder()).willReturn(1);
Servlet servlet2 = mock(Servlet.class, withSettings().extraInterfaces(Ordered.class));
given(((Ordered) servlet2).getOrder()).willReturn(2);
this.context.registerBeanDefinition("servletBean2", beanDefinition(servlet2));
this.context.registerBeanDefinition("dispatcherServlet", beanDefinition(servlet1));
this.context.refresh();
MockServletWebServerFactory factory = getWebServerFactory();
ServletContext servletContext = factory.getServletContext();
InOrder ordered = inOrder(servletContext);
then(servletContext).should(ordered).addServlet("dispatcherServlet", servlet1);
then(servletContext).should(ordered).addServlet("servletBean2", servlet2);
then(factory.getRegisteredServlet(0).getRegistration()).should().addMapping("/");
then(factory.getRegisteredServlet(1).getRegistration()).should().addMapping("/servletBean2/");
}
@Test
void servletAndFilterBeans() {
addWebServerFactoryBean();
Servlet servlet = mock(Servlet.class);
Filter filter1 = mock(Filter.class, withSettings().extraInterfaces(Ordered.class));
given(((Ordered) filter1).getOrder()).willReturn(1);
Filter filter2 = mock(Filter.class, withSettings().extraInterfaces(Ordered.class));
given(((Ordered) filter2).getOrder()).willReturn(2);
this.context.registerBeanDefinition("servletBean", beanDefinition(servlet));
this.context.registerBeanDefinition("filterBean2", beanDefinition(filter2));
this.context.registerBeanDefinition("filterBean1", beanDefinition(filter1));
this.context.refresh();
MockServletWebServerFactory factory = getWebServerFactory();
ServletContext servletContext = factory.getServletContext();
InOrder ordered = inOrder(servletContext);
then(factory.getServletContext()).should().addServlet("servletBean", servlet);
then(factory.getRegisteredServlet(0).getRegistration()).should().addMapping("/");
then(factory.getServletContext()).should(ordered).addFilter("filterBean1", filter1);
then(factory.getServletContext()).should(ordered).addFilter("filterBean2", filter2);
then(factory.getRegisteredFilter(0).getRegistration()).should()
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
then(factory.getRegisteredFilter(1).getRegistration()).should()
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
}
@Test
void servletContextInitializerBeans() throws Exception {
addWebServerFactoryBean();
ServletContextInitializer initializer1 = mock(ServletContextInitializer.class,
withSettings().extraInterfaces(Ordered.class));
given(((Ordered) initializer1).getOrder()).willReturn(1);
ServletContextInitializer initializer2 = mock(ServletContextInitializer.class,
withSettings().extraInterfaces(Ordered.class));
given(((Ordered) initializer2).getOrder()).willReturn(2);
this.context.registerBeanDefinition("initializerBean2", beanDefinition(initializer2));
this.context.registerBeanDefinition("initializerBean1", beanDefinition(initializer1));
this.context.refresh();
ServletContext servletContext = getWebServerFactory().getServletContext();
InOrder ordered = inOrder(initializer1, initializer2);
then(initializer1).should(ordered).onStartup(servletContext);
then(initializer2).should(ordered).onStartup(servletContext);
}
@Test
void servletContextListenerBeans() {
addWebServerFactoryBean();
ServletContextListener initializer = mock(ServletContextListener.class);
this.context.registerBeanDefinition("initializerBean", beanDefinition(initializer));
this.context.refresh();
ServletContext servletContext = getWebServerFactory().getServletContext();
then(servletContext).should().addListener(initializer);
}
@Test
void unorderedServletContextInitializerBeans() throws Exception {
addWebServerFactoryBean();
ServletContextInitializer initializer1 = mock(ServletContextInitializer.class);
ServletContextInitializer initializer2 = mock(ServletContextInitializer.class);
this.context.registerBeanDefinition("initializerBean2", beanDefinition(initializer2));
this.context.registerBeanDefinition("initializerBean1", beanDefinition(initializer1));
this.context.refresh();
ServletContext servletContext = getWebServerFactory().getServletContext();
then(initializer1).should().onStartup(servletContext);
then(initializer2).should().onStartup(servletContext);
}
@Test
void servletContextInitializerBeansDoesNotSkipServletsAndFilters() throws Exception {
addWebServerFactoryBean();
ServletContextInitializer initializer = mock(ServletContextInitializer.class);
Servlet servlet = mock(Servlet.class);
Filter filter = mock(Filter.class);
this.context.registerBeanDefinition("initializerBean", beanDefinition(initializer));
this.context.registerBeanDefinition("servletBean", beanDefinition(servlet));
this.context.registerBeanDefinition("filterBean", beanDefinition(filter));
this.context.refresh();
ServletContext servletContext = getWebServerFactory().getServletContext();
then(initializer).should().onStartup(servletContext);
then(servletContext).should().addServlet(anyString(), any(Servlet.class));
then(servletContext).should().addFilter(anyString(), any(Filter.class));
}
@Test
void servletContextInitializerBeansSkipsRegisteredServletsAndFilters() {
addWebServerFactoryBean();
Servlet servlet = mock(Servlet.class);
Filter filter = mock(Filter.class);
ServletRegistrationBean<Servlet> initializer = new ServletRegistrationBean<>(servlet, "/foo");
this.context.registerBeanDefinition("initializerBean", beanDefinition(initializer));
this.context.registerBeanDefinition("servletBean", beanDefinition(servlet));
this.context.registerBeanDefinition("filterBean", beanDefinition(filter));
this.context.refresh();
ServletContext servletContext = getWebServerFactory().getServletContext();
then(servletContext).should(atMost(1)).addServlet(anyString(), any(Servlet.class));
then(servletContext).should(atMost(1)).addFilter(anyString(), any(Filter.class));
}
@Test
void filterRegistrationBeansSkipsRegisteredFilters() {
addWebServerFactoryBean();
Filter filter = mock(Filter.class);
FilterRegistrationBean<Filter> initializer = new FilterRegistrationBean<>(filter);
this.context.registerBeanDefinition("initializerBean", beanDefinition(initializer));
this.context.registerBeanDefinition("filterBean", beanDefinition(filter));
this.context.refresh();
ServletContext servletContext = getWebServerFactory().getServletContext();
then(servletContext).should(atMost(1)).addFilter(anyString(), any(Filter.class));
}
@Test
void delegatingFilterProxyRegistrationBeansSkipsTargetBeanNames() {
addWebServerFactoryBean();
DelegatingFilterProxyRegistrationBean initializer = new DelegatingFilterProxyRegistrationBean("filterBean");
this.context.registerBeanDefinition("initializerBean", beanDefinition(initializer));
BeanDefinition filterBeanDefinition = beanDefinition(new IllegalStateException("Create FilterBean Failure"));
filterBeanDefinition.setLazyInit(true);
this.context.registerBeanDefinition("filterBean", filterBeanDefinition);
this.context.refresh();
ServletContext servletContext = getWebServerFactory().getServletContext();
then(servletContext).should(atMost(1)).addFilter(anyString(), this.filterCaptor.capture());
// Up to this point the filterBean should not have been created, calling
// the delegate proxy will trigger creation and an exception
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> {
this.filterCaptor.getValue().init(new MockFilterConfig());
this.filterCaptor.getValue()
.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), new MockFilterChain());
}).withMessageContaining("Create FilterBean Failure");
}
@Test
void postProcessWebServerFactory() {
RootBeanDefinition beanDefinition = new RootBeanDefinition(MockServletWebServerFactory.class);
MutablePropertyValues pv = new MutablePropertyValues();
pv.add("port", "${port}");
beanDefinition.setPropertyValues(pv);
this.context.registerBeanDefinition("webServerFactory", beanDefinition);
PropertySourcesPlaceholderConfigurer propertySupport = new PropertySourcesPlaceholderConfigurer();
Properties properties = new Properties();
properties.put("port", 8080);
propertySupport.setProperties(properties);
this.context.registerBeanDefinition("propertySupport", beanDefinition(propertySupport));
this.context.refresh();
assertThat(getWebServerFactory().getWebServer().getPort()).isEqualTo(8080);
}
@Test
void doesNotReplaceExistingScopes() {
// gh-2082
Scope scope = mock(Scope.class);
ConfigurableListableBeanFactory factory = this.context.getBeanFactory();
factory.registerScope(WebApplicationContext.SCOPE_REQUEST, scope);
factory.registerScope(WebApplicationContext.SCOPE_SESSION, scope);
addWebServerFactoryBean();
this.context.refresh();
assertThat(factory.getRegisteredScope(WebApplicationContext.SCOPE_REQUEST)).isSameAs(scope);
assertThat(factory.getRegisteredScope(WebApplicationContext.SCOPE_SESSION)).isSameAs(scope);
}
@Test
void servletRequestCanBeInjectedEarly(CapturedOutput output) {
// gh-14990
int initialOutputLength = output.length();
addWebServerFactoryBean();
RootBeanDefinition beanDefinition = new RootBeanDefinition(WithAutowiredServletRequest.class);
beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
this.context.registerBeanDefinition("withAutowiredServletRequest", beanDefinition);
this.context.addBeanFactoryPostProcessor((beanFactory) -> {
WithAutowiredServletRequest bean = beanFactory.getBean(WithAutowiredServletRequest.class);
assertThat(bean.getRequest()).isNotNull();
});
this.context.refresh();
assertThat(output.toString().substring(initialOutputLength)).doesNotContain("Replacing scope");
}
@Test
void webApplicationScopeIsRegistered() {
addWebServerFactoryBean();
this.context.refresh();
assertThat(this.context.getBeanFactory().getRegisteredScope(WebApplicationContext.SCOPE_APPLICATION))
.isNotNull();
}
private void addWebServerFactoryBean() {
this.context.registerBeanDefinition("webServerFactory",
new RootBeanDefinition(MockServletWebServerFactory.class));
}
MockServletWebServerFactory getWebServerFactory() {
return this.context.getBean(MockServletWebServerFactory.class);
}
private BeanDefinition beanDefinition(Object bean) {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(getClass());
beanDefinition.setFactoryMethodName("getBean");
ConstructorArgumentValues constructorArguments = new ConstructorArgumentValues();
constructorArguments.addGenericArgumentValue(bean);
beanDefinition.setConstructorArgumentValues(constructorArguments);
return beanDefinition;
}
static <T> T getBean(T object) {
if (object instanceof RuntimeException runtimeException) {
throw runtimeException;
}
return object;
}
static class TestApplicationListener implements ApplicationListener<ApplicationEvent> {
private final Deque<ApplicationEvent> events = new ArrayDeque<>();
@Override
public void onApplicationEvent(ApplicationEvent event) {
this.events.add(event);
}
List<ApplicationEvent> receivedEvents() {
List<ApplicationEvent> receivedEvents = new ArrayList<>();
while (!this.events.isEmpty()) {
receivedEvents.add(this.events.pollFirst());
}
return receivedEvents;
}
}
@Order(10)
static class OrderedFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
}
}
static class WithAutowiredServletRequest {
private final ServletRequest request;
WithAutowiredServletRequest(ServletRequest request) {
this.request = request;
}
ServletRequest getRequest() {
return this.request;
}
}
static class RefreshFailure {
RefreshFailure() {
throw new RuntimeException("Fail refresh");
}
}
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context;
import java.io.IOException;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.annotation.WebInitParam;
import org.junit.jupiter.api.Test;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link WebFilterHandler}
*
* @author Andy Wilkinson
*/
class WebFilterHandlerTests {
private final WebFilterHandler handler = new WebFilterHandler();
private final SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
@SuppressWarnings("unchecked")
@Test
void defaultFilterConfiguration() throws IOException {
AnnotatedBeanDefinition definition = createBeanDefinition(DefaultConfigurationFilter.class);
this.handler.handle(definition, this.registry);
BeanDefinition filterRegistrationBean = this.registry
.getBeanDefinition(DefaultConfigurationFilter.class.getName());
MutablePropertyValues propertyValues = filterRegistrationBean.getPropertyValues();
assertThat(propertyValues.get("asyncSupported")).isEqualTo(false);
assertThat((EnumSet<DispatcherType>) propertyValues.get("dispatcherTypes"))
.containsExactly(DispatcherType.REQUEST);
assertThat(((Map<String, String>) propertyValues.get("initParameters"))).isEmpty();
assertThat((String[]) propertyValues.get("servletNames")).isEmpty();
assertThat((String[]) propertyValues.get("urlPatterns")).isEmpty();
assertThat(propertyValues.get("name")).isEqualTo(DefaultConfigurationFilter.class.getName());
assertThat(propertyValues.get("filter")).isEqualTo(definition);
}
@Test
void filterWithCustomName() throws IOException {
AnnotatedBeanDefinition definition = createBeanDefinition(CustomNameFilter.class);
this.handler.handle(definition, this.registry);
BeanDefinition filterRegistrationBean = this.registry.getBeanDefinition("custom");
MutablePropertyValues propertyValues = filterRegistrationBean.getPropertyValues();
assertThat(propertyValues.get("name")).isEqualTo("custom");
}
@Test
void asyncSupported() throws IOException {
BeanDefinition filterRegistrationBean = handleBeanDefinitionForClass(AsyncSupportedFilter.class);
MutablePropertyValues propertyValues = filterRegistrationBean.getPropertyValues();
assertThat(propertyValues.get("asyncSupported")).isEqualTo(true);
}
@Test
@SuppressWarnings("unchecked")
void dispatcherTypes() throws IOException {
BeanDefinition filterRegistrationBean = handleBeanDefinitionForClass(DispatcherTypesFilter.class);
MutablePropertyValues propertyValues = filterRegistrationBean.getPropertyValues();
assertThat((Set<DispatcherType>) propertyValues.get("dispatcherTypes")).containsExactly(DispatcherType.FORWARD,
DispatcherType.INCLUDE, DispatcherType.REQUEST);
}
@SuppressWarnings("unchecked")
@Test
void initParameters() throws IOException {
BeanDefinition filterRegistrationBean = handleBeanDefinitionForClass(InitParametersFilter.class);
MutablePropertyValues propertyValues = filterRegistrationBean.getPropertyValues();
assertThat((Map<String, String>) propertyValues.get("initParameters")).containsEntry("a", "alpha")
.containsEntry("b", "bravo");
}
@Test
void servletNames() throws IOException {
BeanDefinition filterRegistrationBean = handleBeanDefinitionForClass(ServletNamesFilter.class);
MutablePropertyValues propertyValues = filterRegistrationBean.getPropertyValues();
assertThat((String[]) propertyValues.get("servletNames")).contains("alpha", "bravo");
}
@Test
void urlPatterns() throws IOException {
BeanDefinition filterRegistrationBean = handleBeanDefinitionForClass(UrlPatternsFilter.class);
MutablePropertyValues propertyValues = filterRegistrationBean.getPropertyValues();
assertThat((String[]) propertyValues.get("urlPatterns")).contains("alpha", "bravo");
}
@Test
void urlPatternsFromValue() throws IOException {
BeanDefinition filterRegistrationBean = handleBeanDefinitionForClass(UrlPatternsFromValueFilter.class);
MutablePropertyValues propertyValues = filterRegistrationBean.getPropertyValues();
assertThat((String[]) propertyValues.get("urlPatterns")).contains("alpha", "bravo");
}
@Test
void urlPatternsDeclaredTwice() {
assertThatIllegalStateException()
.isThrownBy(() -> handleBeanDefinitionForClass(UrlPatternsDeclaredTwiceFilter.class))
.withMessageContaining("The urlPatterns and value attributes are mutually exclusive");
}
private AnnotatedBeanDefinition createBeanDefinition(Class<?> filterClass) throws IOException {
AnnotatedBeanDefinition definition = mock(AnnotatedBeanDefinition.class);
given(definition.getBeanClassName()).willReturn(filterClass.getName());
given(definition.getMetadata()).willReturn(
new SimpleMetadataReaderFactory().getMetadataReader(filterClass.getName()).getAnnotationMetadata());
return definition;
}
private BeanDefinition handleBeanDefinitionForClass(Class<?> filterClass) throws IOException {
this.handler.handle(createBeanDefinition(filterClass), this.registry);
return this.registry.getBeanDefinition(filterClass.getName());
}
@WebFilter
class DefaultConfigurationFilter extends BaseFilter {
}
@WebFilter(asyncSupported = true)
class AsyncSupportedFilter extends BaseFilter {
}
@WebFilter(dispatcherTypes = { DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE })
class DispatcherTypesFilter extends BaseFilter {
}
@WebFilter(initParams = { @WebInitParam(name = "a", value = "alpha"), @WebInitParam(name = "b", value = "bravo") })
class InitParametersFilter extends BaseFilter {
}
@WebFilter(servletNames = { "alpha", "bravo" })
class ServletNamesFilter extends BaseFilter {
}
@WebFilter(urlPatterns = { "alpha", "bravo" })
class UrlPatternsFilter extends BaseFilter {
}
@WebFilter({ "alpha", "bravo" })
class UrlPatternsFromValueFilter extends BaseFilter {
}
@WebFilter(value = { "alpha", "bravo" }, urlPatterns = { "alpha", "bravo" })
class UrlPatternsDeclaredTwiceFilter extends BaseFilter {
}
@WebFilter(filterName = "custom")
class CustomNameFilter extends BaseFilter {
}
class BaseFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
}
@Override
public void destroy() {
}
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context;
import java.io.IOException;
import jakarta.servlet.ServletContextAttributeEvent;
import jakarta.servlet.ServletContextAttributeListener;
import jakarta.servlet.annotation.WebListener;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link WebListenerHandler}.
*
* @author Andy Wilkinson
*/
class WebListenerHandlerTests {
private final WebListenerHandler handler = new WebListenerHandler();
private final SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
@Test
void listener() throws IOException {
AnnotatedBeanDefinition definition = mock(AnnotatedBeanDefinition.class);
given(definition.getBeanClassName()).willReturn(TestListener.class.getName());
given(definition.getMetadata())
.willReturn(new SimpleMetadataReaderFactory().getMetadataReader(TestListener.class.getName())
.getAnnotationMetadata());
this.handler.handle(definition, this.registry);
this.registry.getBeanDefinition(TestListener.class.getName() + "Registrar");
}
@WebListener
static class TestListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent event) {
}
@Override
public void attributeRemoved(ServletContextAttributeEvent event) {
}
@Override
public void attributeReplaced(ServletContextAttributeEvent event) {
}
}
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context;
import java.io.IOException;
import java.util.Map;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import org.junit.jupiter.api.Test;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link WebServletHandler}.
*
* @author Andy Wilkinson
*/
class WebServletHandlerTests {
private final WebServletHandler handler = new WebServletHandler();
private final SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
@SuppressWarnings("unchecked")
@Test
void defaultServletConfiguration() throws IOException {
AnnotatedBeanDefinition servletDefinition = createBeanDefinition(DefaultConfigurationServlet.class);
this.handler.handle(servletDefinition, this.registry);
BeanDefinition servletRegistrationBean = this.registry
.getBeanDefinition(DefaultConfigurationServlet.class.getName());
MutablePropertyValues propertyValues = servletRegistrationBean.getPropertyValues();
assertThat(propertyValues.get("asyncSupported")).isEqualTo(false);
assertThat(((Map<String, String>) propertyValues.get("initParameters"))).isEmpty();
assertThat((Integer) propertyValues.get("loadOnStartup")).isEqualTo(-1);
assertThat(propertyValues.get("name")).isEqualTo(DefaultConfigurationServlet.class.getName());
assertThat((String[]) propertyValues.get("urlMappings")).isEmpty();
assertThat(propertyValues.get("servlet")).isEqualTo(servletDefinition);
}
@Test
void servletWithCustomName() throws IOException {
AnnotatedBeanDefinition definition = createBeanDefinition(CustomNameServlet.class);
this.handler.handle(definition, this.registry);
BeanDefinition servletRegistrationBean = this.registry.getBeanDefinition("custom");
MutablePropertyValues propertyValues = servletRegistrationBean.getPropertyValues();
assertThat(propertyValues.get("name")).isEqualTo("custom");
}
@Test
void asyncSupported() throws IOException {
BeanDefinition servletRegistrationBean = handleBeanDefinitionForClass(AsyncSupportedServlet.class);
MutablePropertyValues propertyValues = servletRegistrationBean.getPropertyValues();
assertThat(propertyValues.get("asyncSupported")).isEqualTo(true);
}
@SuppressWarnings("unchecked")
@Test
void initParameters() throws IOException {
BeanDefinition servletRegistrationBean = handleBeanDefinitionForClass(InitParametersServlet.class);
MutablePropertyValues propertyValues = servletRegistrationBean.getPropertyValues();
assertThat((Map<String, String>) propertyValues.get("initParameters")).containsEntry("a", "alpha")
.containsEntry("b", "bravo");
}
@Test
void urlMappings() throws IOException {
BeanDefinition servletRegistrationBean = handleBeanDefinitionForClass(UrlPatternsServlet.class);
MutablePropertyValues propertyValues = servletRegistrationBean.getPropertyValues();
assertThat((String[]) propertyValues.get("urlMappings")).contains("alpha", "bravo");
}
@Test
void urlMappingsFromValue() throws IOException {
BeanDefinition servletRegistrationBean = handleBeanDefinitionForClass(UrlPatternsFromValueServlet.class);
MutablePropertyValues propertyValues = servletRegistrationBean.getPropertyValues();
assertThat((String[]) propertyValues.get("urlMappings")).contains("alpha", "bravo");
}
@Test
void urlPatternsDeclaredTwice() {
assertThatIllegalStateException()
.isThrownBy(() -> handleBeanDefinitionForClass(UrlPatternsDeclaredTwiceServlet.class))
.withMessageContaining("The urlPatterns and value attributes are mutually exclusive");
}
private AnnotatedBeanDefinition createBeanDefinition(Class<?> servletClass) throws IOException {
AnnotatedBeanDefinition definition = mock(AnnotatedBeanDefinition.class);
given(definition.getBeanClassName()).willReturn(servletClass.getName());
given(definition.getMetadata()).willReturn(
new SimpleMetadataReaderFactory().getMetadataReader(servletClass.getName()).getAnnotationMetadata());
return definition;
}
private BeanDefinition handleBeanDefinitionForClass(Class<?> filterClass) throws IOException {
this.handler.handle(createBeanDefinition(filterClass), this.registry);
return this.registry.getBeanDefinition(filterClass.getName());
}
@WebServlet
class DefaultConfigurationServlet extends HttpServlet {
}
@WebServlet(asyncSupported = true)
class AsyncSupportedServlet extends HttpServlet {
}
@WebServlet(initParams = { @WebInitParam(name = "a", value = "alpha"), @WebInitParam(name = "b", value = "bravo") })
class InitParametersServlet extends HttpServlet {
}
@WebServlet(urlPatterns = { "alpha", "bravo" })
class UrlPatternsServlet extends HttpServlet {
}
@WebServlet({ "alpha", "bravo" })
class UrlPatternsFromValueServlet extends HttpServlet {
}
@WebServlet(value = { "alpha", "bravo" }, urlPatterns = { "alpha", "bravo" })
class UrlPatternsDeclaredTwiceServlet extends HttpServlet {
}
@WebServlet(name = "custom")
class CustomNameServlet extends HttpServlet {
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context;
import jakarta.servlet.Servlet;
import org.junit.jupiter.api.Test;
import org.springframework.boot.web.server.servlet.MockServletWebServerFactory;
import org.springframework.core.io.ClassPathResource;
import static org.mockito.BDDMockito.then;
/**
* Tests for {@link XmlServletWebServerApplicationContext}.
*
* @author Phillip Webb
*/
class XmlServletWebServerApplicationContextTests {
private static final String PATH = XmlServletWebServerApplicationContextTests.class.getPackage()
.getName()
.replace('.', '/') + "/";
private static final String FILE = "exampleEmbeddedWebApplicationConfiguration.xml";
private XmlServletWebServerApplicationContext context;
@Test
void createFromResource() {
this.context = new XmlServletWebServerApplicationContext(new ClassPathResource(FILE, getClass()));
verifyContext();
}
@Test
void createFromResourceLocation() {
this.context = new XmlServletWebServerApplicationContext(PATH + FILE);
verifyContext();
}
@Test
void createFromRelativeResourceLocation() {
this.context = new XmlServletWebServerApplicationContext(getClass(), FILE);
verifyContext();
}
@Test
void loadAndRefreshFromResource() {
this.context = new XmlServletWebServerApplicationContext();
this.context.load(new ClassPathResource(FILE, getClass()));
this.context.refresh();
verifyContext();
}
@Test
void loadAndRefreshFromResourceLocation() {
this.context = new XmlServletWebServerApplicationContext();
this.context.load(PATH + FILE);
this.context.refresh();
verifyContext();
}
@Test
void loadAndRefreshFromRelativeResourceLocation() {
this.context = new XmlServletWebServerApplicationContext();
this.context.load(getClass(), FILE);
this.context.refresh();
verifyContext();
}
private void verifyContext() {
MockServletWebServerFactory factory = this.context.getBean(MockServletWebServerFactory.class);
Servlet servlet = this.context.getBean(Servlet.class);
then(factory.getServletContext()).should().addServlet("servlet", servlet);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context.config;
import jakarta.servlet.Servlet;
import org.springframework.boot.web.server.servlet.MockServletWebServerFactory;
import org.springframework.boot.web.servlet.mock.MockServlet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Example {@code @Configuration} for use with
* {@code AnnotationConfigServletWebServerApplicationContextTests}.
*
* @author Phillip Webb
*/
@Configuration(proxyBeanMethods = false)
public class ExampleServletWebServerApplicationConfiguration {
@Bean
public MockServletWebServerFactory webServerFactory() {
return new MockServletWebServerFactory();
}
@Bean
public Servlet servlet() {
return new MockServlet();
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context.testcomponents.filter;
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
@WebFilter("/*")
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request.setAttribute("filterAttribute", "bravo");
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context.testcomponents.listener;
import java.io.IOException;
import java.util.EnumSet;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebListener;
@WebListener
public class TestListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
sce.getServletContext()
.addFilter("listenerAddedFilter", new ListenerAddedFilter())
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
sce.getServletContext().setAttribute("listenerAttribute", "alpha");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
static class ListenerAddedFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request.setAttribute("listenerAddedFilterAttribute", "charlie");
chain.doFilter(request, response);
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context.testcomponents.servlet;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
@WebServlet("/test-multipart")
@MultipartConfig(location = "test", maxFileSize = 1024, maxRequestSize = 2048, fileSizeThreshold = 512)
public class TestMultipartServlet extends HttpServlet {
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2012-2025 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.web.server.servlet.context.testcomponents.servlet;
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/test")
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter()
.print(((String) req.getServletContext().getAttribute("listenerAttribute")) + " "
+ req.getAttribute("filterAttribute") + " " + req.getAttribute("listenerAddedFilterAttribute"));
resp.getWriter().flush();
}
}

View File

@@ -0,0 +1,12 @@
-----BEGIN DSA PRIVATE KEY-----
MIIBuwIBAAKBgQCDX+Ux7y7dkfCnQgRIXzAlFrG8uPxwFdC8J4FJNrAurnjL//PV
8LEHBDVbyPjHaoNbH8Pfc2pJnOzndWZVf0nqBd4Q/Tz9w/pJ9g6E8HOh+rzU3eK5
mF0rezcMbJsot2Vdx6XrTztDKi2GY0etKNV399DYtepIYA6v5ovuYAOjLwIVAPyb
9zR+UjyCkBwESDm9dpzKsGp1AoGAS2vtTiO7/MT8cJwo4mxYtiJnV5R2mk1JJOTe
4AFPgmnce/YbBzU2JwL9J9HGewDkmxudW0zoxZVeNw4dRua6oB5STV8XciW8vSo6
mdDBJFoBW9/DUscRP4j2aRfkXGlYuiEF6ZT8g6pPHZG7pLviihMAWNRVLmBt38wa
8FA9aZECgYADbfwh7OhSE1J0WRaEk/4Usos5Oi6fhUyqr2u34Ereug9Gt4tkhePa
b3y31i2PQfsatpR+4VpBC6zpPgHQYpuqlqDRWJCd+Cxo9751nOiA3xYVxNoiwIn/
WxoUkC8Jv8kYFAJRceXkF/auVh77MUoruAmoT2lGE6zP6ngP2q6jFwIVAJMF5kZ+
AUZbUBUpZaPuZ15RL8GF
-----END DSA PRIVATE KEY-----

View File

@@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,2036BA8802B364C6BE415A48E1AC9966
0euP/daGIbl798XGc2Tc6u6NmnCl8C6hE0a2grO+wbvFEa13IT4hNVNTzqahfLb8
STF9LXdvPbz9HybyWnG8KNm5sqsSY1omsHQI38khhwuhpHuWfjb62TqWobwrAmAU
qh2+kjc7upzW48axJLRQ69vfE4zKczZ4Sqv8qImPz9SiXPeaXinHf/wKfgXZZOvr
6A8nCykLglVrFbrZWydqKrTM9w/LTXsn0gNpDmrfZERDgWnabKnjYidHkXdgAoFy
ulsBjKvj2hJFtnJHTkCPXj6XAj18cwUWsN7inFJR5wqjIWXuLqfv/fNbK9lPzjAk
vMTEVWwynekvXFsSvnHAyEJfmRCNF6DOE+5VwjEROCFq8xceK4fz4T3BHC7XSgis
2ofBZwjCAJ0pP6SdRpbbZ0wlXH09wqDDTZ2vWD6bEG7hO9VuP/aO76MIWydLa78z
bDb2R6MnYKmYEInPzwfU4H66Z3/cz8orfYLlnF55DRd4rO+fJm6Y/XY14ac3x0S+
P67s5e0WbdImagUsOxGNBjgOr84lwmtk/LoVd5nKho8JyQw3V7tiyrsKsM1lAXdS
TaJXKRnN6LbgC8d+Tgfjc/qMUdVqLlN8zATIa6E4sc861DDiJsneXuFm43pHc/ja
0sxSifBKgLGenj8xY5ANEfrYnXKrzaQemUnMzd+zan6OS02I+e8WTQevESt1j0D8
7mgLJ9W2SfNNfiEd5OKar21lLNOc3POaFn7M73zL/gyhTROipPR3fpV+08k27UKl
nzGhFtRwQieXl34QjM0JrHokKKfv8FsAJsbrGnz2/wcm6jJGmrj5VVsogMKc90v4
ZTM44NAKymM0UTuIp/rKb/UUVYDpl9VWDegWh2+XKX8io31ENrDMcvQTJ/mzNCuZ
SINrAeMbVD6W5L0i7THEt32YsDmbFsBaEJBlNXlUNBa/NCK0pjwAn5AYOFUvEqBC
oZUEleUMU6Q3TKI287o37euw3No6jo5VdPULlrwHsZAjmq9EUvRCgMoIEPdxv5XG
a4PljE7DQLlk6G9d+gjRzClLFfkadSTbtH9o2011RtWUKP4dcRuSOcBm64xVIO6J
rhe38sE3yzHvLUM/mvLsEM8B0AHl85nrEstlbBzftXMo2CAJ2Gs9c61PYfft+xiT
pRdHx4HB1P+kfHa96ayvAytOexHih2iVKVG5CchOr5tbWmkhXVE5cZAKzvczDYFd
YvniHNiqt6LO8EbJOzz+Yxessmd0zBXj/rjxTMdGwaRjiFI0BIguYpvmGoMS8+bp
spRj2DMtqjNZz68BEEfgKQwDHPCTblYSR+3Uw4sja608sflqQ8rTmOwbfta3SNS8
Kq/zzWfYzarQ8hAk5n/H1Jm6AQdvcptyMjuF8FAiMnvt8xDCBRMD3xY2BHb/tcs9
dBWBxhb76CKIrV3pzm9gGhhZ32Ndq9KmmE+bWCYyvrLxvPJxODfM7X5XamPmG5SH
wEKSbp0wPF0b6vyxt5M72OYnU8UnxYWu6PlbVvczfWEu9fIw/oIN/kDT/QUojTyt
wBgzVSTSNFaDtVXJw10IWQgsgWdNY5XueHKH060P5g+14woxU8i3TboNd+tC3hRl
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGIAgEAMBQGByqGSM49AgEGCSskAwMCCAEBBwRtMGsCAQEEIBfCkWEWyc2tHIvS
Ao6hhcj09dnh8NOmtZeqGmcXHnIqoUQDQgAElux3elmSzb/WqEZXb1vdXx/tcIpC
Yq2vewG8H1SikMoACeFVRcjuy31gJ4Q8M7UQmrR4+WXSptV/UQ6Gkt3XuQ==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGIAgEAMBQGByqGSM49AgEGCSskAwMCCAEBCARtMGsCAQEEIILhgc3joEZDWMDm
9TYgrENN7gbqtMpMw1e2MTLwlJhCoUQDQgAEiDN20JP8O9zSK46tP6MkXJPNAfyN
IQ0hOgcQ//Fw5V0yiSU+BPGeDoIDsW8LnElS1hIZWq0JVQNZge5ei2bshQ==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIGiAgEAMBQGByqGSM49AgEGCSskAwMCCAEBCQSBhjCBgwIBAQQoNifTnb4a5dOR
yr8QFVM7Zkw/f/AMm5T5PQ6iVTCzrnw/kZ8glwl+JKFUA1IABHSnWUC/tKXpNHGE
P89QVKEgvetwCQWFoOENAgXORniLiaLdAdsR80ouTsZiFgHG9su0l5ESEnFWQr5x
UMj/vPwwhSYm+YP5ucx5NezuBM4d
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIGiAgEAMBQGByqGSM49AgEGCSskAwMCCAEBCgSBhjCBgwIBAQQotGnirBX69ezE
7a9yIBQcqeCMm7hc5YAG8D4396ytBa/2/O/lonDlOqFUA1IABDQHDepa3l/S8Gt9
WrNCNpCPZNBXvmkGPnVXZchZI5BtUySwYxHX1tpatGs3jY7drVYm+NyxZE81pecY
TvXR8bu7e3BIp2SwZmXEDxdYp1fw
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIG6AgEAMBQGByqGSM49AgEGCSskAwMCCAEBCwSBnjCBmwIBAQQwK6y6NydLMTNm
LhDNPyTDKEemTWTUuMGfBQxEz+lQKAqz/So4uA+fQzor/t8to+uioWQDYgAEaK8X
3KCyRDMpbACw2xG4UUe9OxyuGWFaGKPxhKJDyW5Z56gT5P1Q2y4CblL/X9VcDIMX
dcQqRNBkPQfy1+fJXwKO0ClfD6MIE3bv6PTZ55J6H2H1dpg38a2soRchz0FN
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIG6AgEAMBQGByqGSM49AgEGCSskAwMCCAEBDASBnjCBmwIBAQQwBGUaEvTtnxm8
fOWj2c2cX4991rvGmfGviuWWQRblSii/v9FG4nQ4Q2IrgBy+hgK9oWQDYgAEQL0d
QoOTArIx70V/XxipoxxBeKT7zmIe7id5pQiw4O4nA2S2BFxQF9eW9ipnm6DaN6ja
X/+2k+cC4qIfqzeLcLUFXxz0qdec8lNNtr9QmwoQlv11beeHmQu9C1GwHmvG
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,7 @@
-----BEGIN PRIVATE KEY-----
MIHsAgEAMBQGByqGSM49AgEGCSskAwMCCAEBDQSB0DCBzQIBAQRAmEQNFMGIDLoj
Ktdg8V71WdSs7FiBkE6Bft25+yY8ohugk/u8aKeIKNVtSirMgQhGFJy/BIBkvM6V
1JfnrglkjqGBhQOBggAEYbjTnA0x42NdM7jVv7jAoZq0iOYopbwejlOEsx8/MqRa
Yt4Ef83holIsgOHWSeW+kw1oMDmieoCrhnkM/3KgGzV+BxCeieAWGxABsj9YhAmb
ATorRJ4q/pMxRq8gIUv05/dGuUttl1gdbKKnGQjxDBM4v5H+/4z00nzzj4Gbfx8=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,7 @@
-----BEGIN PRIVATE KEY-----
MIHsAgEAMBQGByqGSM49AgEGCSskAwMCCAEBDgSB0DCBzQIBAQRATamyZ088BsIP
Scslwa0I1xfC0/6udycncr+i/QIFOXNr4OQiVb4KC/CS/2FPotVMFSHZfCS2bgi6
Yvg3Mta5SKGBhQOBggAEm/uIqsytMZypsqCuL0jwZh8xCRVEkUd02YPXcOBhMzS9
bhAao4CLAuXhWzplr5qk/7ttvczl7qFDOvBzNAIieZHwbFrouZ6Pew8pQXRcMDB+
FnXwNgpljTNmz/f1ePjVKU1ZgbQ+xVf8Qt8OI9S0Pla8siTgbVweGMLtq0A8Wuo=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,23 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIDzTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIJ2oQD0S0aMcCAggA
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBCNXluHkzV+EEdxlhmcIAWBBIID
cAWJ20CCQlOCxxCddujc5gPPotUyKTbXA7xI3J9DdjrqBaHrNY/Yii4Zxk9lpzPL
3x3M3O4C8nJjk4OEA0fiyBYOV1d+KZfa7UZojIe/e+7BuTH9WAvgVzORYYEFX6x7
DTTmYH6VSej/RBdNTV1KeWA4+20Zn+vqCwcBG6R78cQDP3xGOmT6jOAQR/0kkqEv
GN22UGpuMlHb7SDiYeDRqQPdRMkpu1RZS37MWEZ//yuwqEyaowK0JgTVGKyibf3E
qXYV7TqEumdfrpZxPXkmJwswUSoFwE1wM0XU5WMg+lnNiroaVQjzqLEEqENpJ03c
Y27l0m83GRVvISvwLGOKY0Sbcb2d6NnqBjbVjbJopjBrww3iiTUqGiQZTFjiJ3CD
VNKdG3HPWEdjZG5xoF9BLF3ZVz/jrfLkK2RcZBs0U6SyENd3H1gOk3yv4Uw2VjXi
h/PA27oFJ2I5DN9bcHhbKVMFykc9JZbisDO0TNx6gbVxZUdNEER0nsP4/sZCwXp+
K7rGvrUk17vBesp0K/tTfdBM2xVkV6oCMKPeQpsdekwCSAdNamSpxZn5LqUaX+Gw
bMf1FxTlIL0ujLiN6/U3VWxLuJuoJauFyM5wYlpqgBOQIszVREQqMOs/Zp1WT5uS
VUA7tdcIpSE7aA3q2rg1BkaiLMSEBMY/XWAI6SCj7iQyRD6GDse/ayJrQ4rUPvD4
iRLQN9B1BYwAplGX3Cmi++Yos7tOq8D2HfTDsI0bHAi+oSDTRUAsiezx5QRLgrp1
pO2dyBlJ5HOlK4SA8h5CMlk2fVgCLGCZ61VcqF1DHu5NKuCnCR73QrwuZAPlo19L
Jt1YAgcXBfFoSbjAvE+AXcxu2uw+t2kj6cjGRSRpBNSHCn3bJ2zu0qrPL3lOMhtt
DYp0QNWJkW/6/fpmXXAYQQv6bFY0Bia8Ima9LATcCwpYcRox1pL2rvU1EWLDVy6N
PdFdlzLM7UcrY9Vy40gEj1qz9epAXqzfPkmbyP3i41BjKlZfmmzpJEI6hIJMtT6T
xWU1Kgg+mAED6STAJ6nem2bIEzMfsNM00VrHXoLU4IWPCe8NMYOaVox1xBWs1mer
lu9dgGBAy4/gn6v55XzjQ4R16wyiiJn3ZQGrUD0AE6N2E/tfWiphVG1tYDjjWCLv
bXG/U6P6DjClkBocb4DgKBc=
-----END ENCRYPTED PRIVATE KEY-----

View File

@@ -0,0 +1,21 @@
-----BEGIN PRIVATE KEY-----
MIIDXQIBADCCAzYGByqGSM44BAEwggMpAoIBgQC1d6MOqEDLxjfjz1v7Vg44bBBX
VOoNdWhvJPl9NL2Js57UmYGrTqFit2VCLxbS5FVLyOZ25S0myQRsEtKyi4V9+ELI
0q3oQplm+l3tZqKjtO69ZlbzYSl9IG1254/FCwBnBTuN7Vl6A2vryIhY7cL4E053
Yy0xqfP7swPgMYNBqjc9c53hNHKiseQJ8Q2wFKZqm13xgqnBqmWbm7yNmzKJbjMW
Zt3/WpJdjqfRnjSljFOkYPPBkGRUIKHcZ9fw4odVov2vblGzXwR+sFeE3lcF50WN
uppjszMXlqR4y937CUSFbCabatRHEcPTq/FxioERCrCdx3AKOfwAquahtvWb9V7A
47FlibDDeybh5jCH0j9HSjhjSiDZdadSgGKFynPNlVAlOETZKGqkeqAZZ+dsPkVO
n8tx+VXZJF00YSe1HLKJUWXaT1tEGF6vw/dXhhQVir4j63fN2tZdhTOW1ao/J/iT
VYEQQjYeMKKuZQveBKRBlpAjhmjOztbE8VL4O/cCHQCxPXlCpORbfOMEEGU0hbEE
HsxFHMXHCnURCJxVAoIBgQCBqsk+z57UndC1Ut6u19wILXs7UBgLo0ivId2QHtm5
kY77P9/lNOyCIQkBnULbJ36lHm6yxLZ8imyC5Lc7wlFJpJ6PpiTJ3nPi3fzhbftB
2KCJVSwB3XfkjvyyS8bfwwqyrmce9el+AIFJuWPrFSkjNthq7U5vU5a+uNT9XZrs
EaDbjkjVJXRX1oDS3IfWXWpb9i/LOE9HU+NfDKfydasWASvwNX1F5BKXD0AH9adj
9Q7b0p4DVTh+UPWLBk9/e6gsA5HaRI1urAMNxs5Xnmd8UYF1I+AmjQ9Mi63Pa0YW
QjpdH2hoOQGLemQ/72woFVzLaHWBcTuSwjREilaAA5M8CWq4rpuA79MrcHgzSp2C
W1gtZa2/3SymcJ7Py2PHbncod8gR9dxHWVO07ccOXUG0iL9m4MzQ27uVvTh8Nrma
M+JET778E2FaAkAIT34eNMC6Yk2IDrxU9L66FFx3+3n0cOeWaJxIWrIQ6uWk+uIH
VzPsZAQU/V0/QBABlHuSj1cEHgIcYTbB5VrbIgust0jVvQCnlF4b1V0qz2iDJt6o
sA==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAi1t9NlcmE8TwICCAAw
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEJacGnDl5HIWWbv604Vp0CUEQDEx
jZKBJnhmLTPMJbE1TgVe+9N8ZG1CVpSFz0xo9xCk15G4E9jgxXw/a8Sqy7NiDqRb
FLJrSStMU+ygP7wXIFo=
-----END ENCRYPTED PRIVATE KEY-----

View File

@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIJ55hBE+FwS4M3k/c45ZJKPHtsklKrb6qJlER0cMJ2rn
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MEcCAQAwBQYDK2VxBDsEOSSF8O0uKk5pRrjUNV+QgonwO+WeDRb/i1U7vM+TLzh7
jAV58E6oglA53konKxGv+GC38dCb72gSeQ==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,7 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAjZ2eXtLFXLdgICCAAw
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEIlOTfeoLwq9Bs9TFtw7VSMEgZAU
RO9W+K3vvzRG4sACrcFaB7PMhv3+HUOcPFf09QHa7aSJMWsb8EXGFBK4xlFhkK0/
N0C8sRtH2N1JdYj9hiwS7I8WyybaR2W0ZALNR57iLz3WsOGQ7nVECFprElboqnSW
BtjkKTD9pz3xX/6cMkDV/2WqbS6Y/dzWJTH8yTTqjzTjbs4vguEK1Io6dlIRVqA=
-----END ENCRYPTED PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQguc9upTMQn8b+loAx
6c8q20dHYBf3V9374I3kJIDmC1ShRANCAARnLuOxL7n7Gq12zd9vq2neAv6PYc1h
W6M2gJKSbfFYGhte382jOJ2TgwaTQL/J5IPSfuJKkmAPBIl8CdJKWlwA
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,54 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
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
-----END ENCRYPTED PRIVATE KEY-----

View File

@@ -0,0 +1,54 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
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
wloOOrNW1gpIoUUMVuRjFA==
-----END ENCRYPTED PRIVATE KEY-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADALBgkqhkiG9w0BAQoEggSoMIIEpAIBAAKCAQEApfv1BYcLTl09aBmW
OnwUEKxByqBZeCXLp6Ck6/plp3GgC9gtFMfSk7mImkIt8BAFWK27A0/vOFDf7SDX
tFYtlL6ZukxSgaltyEjMVMX/oqB3vMsqzDX96So4UauDlQexWORT0LcUp3IcjA8L
JrrZS6j278y3ah/Xkex56IRpfFbdqPj/p4rJMw0WFtqvINV6C2xGxeoC1/LcHM/H
WQLpeRD5PgnIwUw/dqMYtV7nfUDU5wCJLe6I0ogdgmCGrAeogldFilakPs47yU03
/b6qWFHaj7OwGZRV51R/GChS1HdVN42nsXHiIz26KPIf8BS6O/iAZlUaS8xhw5XB
je0uIQIDAQABAoIBAAh8WZn3Pfo7JRUJ3dbOmh4CGHj5+qj8Ua2XtmbEDediFTsV
ybQ6xQa9YQD16jBQOV2/wARa1VGNPO18FNsI3tqwZd6S4VL0rQKkyiF5X+jaCFUU
E/ONvRXrDScLvDXlx0jSn4BXo8wttszoRfssaUiHclxvHF9mEljI/LCI+HWdTAys
+3l/Yn1ewwA2iFFU+ZcwgvZHXjLjRLfImTfr7oQLeolpP9sxfwb2RdQ24ifgIh9N
Yv9KzFfFJNl+2o3q6XBKqvjXYWmTam/hwXhGnFNb3LgrOwkSUIVpUJl52F/fu+BD
AdJu0ELPUNIu2Ll0fBp3Efj80vcSZqtDSJ3Bl/sCgYEA6DZQm1L1Y2tPMcX+JLtV
BKC19YRTJLI+CQsU5YnD4DN6O3a8PITfRf+SHWI9slGGs4QU0rv6NLMj4c0Vxsbk
74LQArprdw768+hLH8z3r/fAZ0QrTJZSKMuGvs4To4dHvNSdc2lYDtadDysPxkKZ
23aL3ApmCqZpHvIUndOGKV8CgYEAtvzWJd6faGWUEbQI2qI4/H2w/t4oxIgVDOeu
qCjIfw3jj9QUQrzC/ckHEJrb9ILYuzxfe92qPf9qmqHyE3aKMCN4MFIz+PdfwM7F
P3/QSriS+PdCnS0ysmHrUdJRXOsl6SYDVnCfyhU6HtL4GFO5expMesogpw2xXkYk
gYOaWH8CgYAP0SNMcSoly3lpeoMFHX19AzVhs9G1/i4bj5WszOV6sAbzZfMMbECJ
FA9v0PFC5Cq4r5Z7hDJWxJz9FGsXTxTo+5APn4MSaQLO+lOjpuJ4KfgBELOiU9rk
zHgxJvhPezd3tUPESLimyheIoPZCGuc/+6MrKcopj4w5f2PIHFBXIQKBgQCN7qTn
8LpyTj/AT4WCl8tdxNxRg93ZOrghL18gnamOKyaz+8rPTPxtvsyVC5jKGeejqxtg
xzlyJzf3wt8yS4K5/fkOeeRIGxARTBBgxXG5U1rkc10e7tzg0eSlrV1glh/srIhw
NqEqLLbNC9RVgjNfEbH6l+clzBAkUIGmV36TXwKBgQCI9r8ZYR7xGYYDTpMSbGdL
XpWuNWwgZQsvBAH+pXaE3A/36tXdggA5nZH3SA+yIoJHGiXHeM8K9LOMAbAzHhsJ
ia/yFcH7lat92/28mrxoAkHHk5oUdIcP6pcPny3cE874sh/UPG7BNKrS+h2ll21e
OFsE0r+qLh68/S0HZM1/eA==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,54 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
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
oDEneepILtLCVoJewFPvdkpnL1xcdjveuw==
-----END ENCRYPTED PRIVATE KEY-----

View File

@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCb/Jbg0D7zw7IF
DBzn/KZ0kjPf+vkOaZOxNG/PosID2/+OKHK9oXgnZcVbEtvJgR4KKjc4nrSMFtFa
BbwsvJWxUEqGjLQ5WahSe04R2yZ4LViGbFuBOHAWq9qtxRyDdkghrQvNy8cOr53K
O8/UAOJCgkYr7x562QLEQ+FUvpGqID2xWI5TUxVezmHdU/sZF8bRyH7TtEZ5zMqZ
Lt2hjD8CrzFt9aMv2eBldX8c2YTi1/3YPKi8mos21jsyRFa8gPeyi/TnKfA5mows
DWxCN2dvOw3ynKapFQV1QZief7KRxw3FGnUOdUfw/FqfpddZK7TJ3/AoaM6oNHy5
aAqcVMqPqb4ZC0eO1R6+xIA4SoZBD+ZTJC2khoVQRRwfRyKQJpp9eWJ9+SOGCkCY
r+yZbcvGAuz7Y8SPp3kUWZwflofRCeE3p77U+icKkppkkmEJIb1iMm6qYoTjc5M/
wTpI5qeQmGaftzz4dlWafTAbHT2jbwvSMgYFQQvW4RyV8Iu95PYl6aUg12mXwv6a
pYf0QuOPGCMP4UzfLfFTYDniS+34aaWkl77Lzmw98BRHa2Xjmn+iGQcHYjKvh7FM
bRBiFxcSEWjkh+mgDypV+8mqS6FOPC7WeCNvt1XGIRoKnlF4OarZjdI5KP9wKtV5
hhx6GQpeuZSWyTBLKbVIsuUiGG2VxwIDAQABAoICAD24HM7JNw9mkCqVF17nRcl8
C9CE0kTUm16TO+ZxJMk4JA7QjE3h9NPJ3ePiO1qonwUwnPbnPNLtOFqhSEp/N8+X
0FUamTjT89jm9wXzq24DqzJM74vak+c0imsVQen2RCYm/TOpfJKgBBP/xITC8MOW
HkPF8k5zTTfxD9hjKumgpihkvLPVfPAtQuW7E/BiywU4io4jl3sb/9HKjGEeR9Q9
E5bJiY8mazZZ3jjBDGZhRgxoO++cSpcg/v0tsxAVC2z3GajZnDZ+oxXPHdW5bFDD
kgo712mxap5xnPyh1DsAAr/JbyWQXC3K++SNTv72Xys9Ux36EkLVub/2nbQrjJXb
Vc6eoJRpoeXwvfeqnJOaRUWqluSxqhxI7ngLtVxTAAM79H0GBdAEaB5DvU20zdu8
7k3ggJ9Xoyu19KiCsC6L+Odix+vTyv+QmIQBGHB4Ts/4YrKMLF49HWpo2qjV9Zef
bCKjjzz8VrFk5FyFWJQk8o0P9NLn6+epD7n/ndjeUWy04pLc9i3ya7wmojZZbWKE
UpwcruX/el7A8t7cQjMKHO8/tzFVPsI7o2osqvfY/sZRBWuDe3iHgY40ROwvUvjr
6k6qwIHPmJqgywmMbv0KD+nnjGqIYThxuvh1n9gp/JlI/QWOs26Mvwk+QFMbA+6g
XfihMLpzLXWY+Z/7uT0ZAoIBAQDIFiDO/Z2VRS+vNlYOAAEYP0amVTH6Isza3bZe
O3nvC47Gj4/vcxJQdrMHEtI2/geLXDv9jexox8YvcxQ6KeJVPSNRFVHGIEWLUK29
pEzPZDUl3xYFl/WTHLn6gxV7uqxO+Xz5TTCaMRCssbz69QZPryfdIZZ1+WXtKX6s
paRhwwizln9c7vHoN4lPO51Dk1iP6JqcJZdRzPXHSjYSBnuapWBy62+rkHQBbOFn
yv7WzhnbOEYM8GlvteDNH4xG0gcT4G81dOtGw4frtfpphU8k0Vy3LypjlVQr1Smd
dZdbC9TT8kC2hyB3saCp9vQUc1U48CHHW7BGBYTSyaRosndrAoIBAQDHk6IQuF1A
OM/FNwD2nao8I2bOJEYyPgaPFv/lUytC5fmCUuU/FKBdyW+0wtIQDxp/zG2Mq9L0
le0E/L4WI1Zz4jt0tef7qDLm4tadK9foU4vFuFpfwnvgP8uAgzxgK45CTQU09X3N
PRfw4Jp6BK1giEqLhuxXrQvhTocswnIgB2s4LUv6g2LEGpyfXCLBoiyBNTYpxHYq
3E4VtOycxniwUWnR+PQOt9GwIDpjKHzZMHfEOOrOyac85N1s1JDxC1s5XftPII79
jNxTDeN7O/BP1eEQN1U5Qbw36cjrNzgxNzK3L8NqZP0YlSHpm1s816Am0+TM0oF6
mKV0VCYYcd4VAoIBAEWPa9iKUz6RzwIa4c/8MGU9mlI5TCap8o4khkI8ayev3PMq
9d9JIhTXL2ZGJM75gaXxaum7bXT//uaAG4gdB5KarqyBvOwkTAkjA0Pq2sk/DTsd
U4qeScHbOszcxZs+SqkqE0iYjU0Nwb5IDGsyw/7v5ev6wVRCYC0TP/bFn2BdbakB
qUWlzHPu2s2w6/uSPjfJpfajGvhVSRz/r8yUdGRPGjjZoPkEP1A/ih2LdQ04mcSc
y72z1vP/RygIz7vPSKagYAk1nJX9ZEOOAICu19T09Ea7HwF/6MNUWCNlvjjo5BTL
I7RRRfhWyIROVozFi9s/oH6uYZn2UTb24zGC2gECggEBALiIfECDh82a+hm7Cwv8
qmwiu6r9hV5tVXk25fNv3D9mDzd+WHPkKYeuergjr0GkBXeHWP/J3CvE+Lw0yboE
gKpz00/N5qsdUbuEoLYA1Qj/PuzZ0c5bMFkgA5VXQxsVCtupBZh7KQ/9XkaeFped
/YWVX3/1iFBlM+fmyTwMqqOM2Im/8FG47Diw9oKvGX/66LWrsuIZwr1MqHKPsHwh
U3SMQpEgZOG6+4qjsfj/dbkIhKUNj6cWc6jtYQOA5GfMfVPk3zrBuxUcCphM7jqD
KGdZNlndH9LqQhNc+ibrDu0Kwbz5z/FvYUo6knnC6TCvm2hrYlI0jf4CaHHQYM0X
dCUCggEACvTEoQ7gZMeaqr+j8fhsisLoRRCtslqoU27jqWTriISlnlvjNHqYOWb/
JXinuvpiYZG5jih6KXxa26H+Q5Pb4amVNPq/d6qBu5yv9qpD3mCJaHNHPByLScZ9
G+pBW+y5JXHCdFJjo6G4ipLLpPglAPte/TmEnoShGsxtgOmupYSliNteAz0ykvSv
At+UfdzdSY2uHC7JLnJjB7SeOz8YeXyE8KCBOAokxCjs0CdBeZDK46sti9umMuIr
cDVIk/azvt5ex5sXP944Ds7tUs/qS1bdm5DsG4XYBkSKRhNqlkdxLumMPnGcwsZC
JSRSgO3qryi1B/PjFld2fmtKpkffCw==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MHgCAQAwEAYHKoZIzj0CAQYFK4EEACEEYTBfAgEBBByDAgBh6UOQqYJSPoiNWrK0
rA0rTMLw8JdCEveBoTwDOgAEABwjlgBV9PPpMfo8fo6yWRdT+sb1cUdEhobd+V/D
/i58cWpDqd4CApevJWtkGbwhU4J9mN0aYrk=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgAjoYh3zy6gJciB7F/pHi
fU+IfPBeEhgQBFT3zNvox5ihRANCAATTIv3AIcnOXMvnURBseRspHsowmPAxPgsx
LH5+aU2mW06f2PsIe9F8gG/Nf2UOOuO+aqwEStIkfSBR+Fwpl2UR
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgSFp08cP6os9Fxmky
o5VFi6woYEqvbhVNYTp9NvwzBCChRANCAAQo6UQaiHkZr7lxrKDZyL9Qinr4Vy0Q
W3K2EPFYbVIupvzW0RH8vBy+0/yoaNyUsw/IsmBO+A60mhBB4rTjmq6R
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCqdZTmuPvhbOVI/Vkk
yjowWmBb1+81m+ay2QF15TZns1iX4HQlaOZ1GzaqhJRS0R2hZANiAASYw9dep7R9
IoC8Dzt6T0NYOOJE/TPdOXOH+M6uJvKAtmXGalFeLQX6HKlUDNPnBDTKp9p6Nu/o
2k/p2C9m0DaX70sNuyfOmh738Bw2Hlz3If+903Jj+hKSR4kWDkKYb/s=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,8 @@
-----BEGIN PRIVATE KEY-----
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAdO/4HyMRu7/QcjYn
f8vynFJRVOKHVVmRFrAzZwkdusoqf9gkicCxcxhOpOGLezzTH4XBynbcmmMn4PNS
ZriTYO6hgYkDgYYABAAp8YOO/QeQoVEdzsOwZt1ta0/b5r0ESM/QNkBVgrRdCsJ+
y3p/xis4wFlhv7lsrtuDoeuMimnvl+fAfptCzMKHugGBvSE0SjLgydEmUjh/y/a4
O3cGqwUXnnxiLKJ98NFaooGYY+AwH4h77oCbQR1lf8jhe1qsJkR9mXpYuGnkaJ7l
qg==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VuBCIEINAdGSXck38nDXMgbRKqKiBPVuxDirhOs9VDE+NaokZz
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGrMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAjrmjUn6y1PFwICCAAw
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEJuU2Lvx741TqKFa9X8bRGkEUPYI
SNtLGe+fIcgz7rF8YaTnA0oeMsRp4RxBw/fsaEGrHUTM1ddjuyRzdKKNnghZIs0w
zy/O8QNXDzrss5bnxZyHZA2XEvftHTH1Mw9jCtwA
-----END ENCRYPTED PRIVATE KEY-----

View File

@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MEYCAQAwBQYDK2VvBDoEOFRfvQhj134qZjH0wDbmPc90BADiqrpGZSae/sd8GG84
au0ISBY6I7BJJZdiLLED+0abd0hLYAyl
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHgCAQEEIBfCkWEWyc2tHIvSAo6hhcj09dnh8NOmtZeqGmcXHnIqoAsGCSskAwMC
CAEBB6FEA0IABJbsd3pZks2/1qhGV29b3V8f7XCKQmKtr3sBvB9UopDKAAnhVUXI
7st9YCeEPDO1EJq0ePll0qbVf1EOhpLd17k=
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHgCAQEEIILhgc3joEZDWMDm9TYgrENN7gbqtMpMw1e2MTLwlJhCoAsGCSskAwMC
CAEBCKFEA0IABIgzdtCT/Dvc0iuOrT+jJFyTzQH8jSENIToHEP/xcOVdMoklPgTx
ng6CA7FvC5xJUtYSGVqtCVUDWYHuXotm7IU=
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGQAgEBBCg2J9Odvhrl05HKvxAVUztmTD9/8AyblPk9DqJVMLOufD+RnyCXCX4k
oAsGCSskAwMCCAEBCaFUA1IABHSnWUC/tKXpNHGEP89QVKEgvetwCQWFoOENAgXO
RniLiaLdAdsR80ouTsZiFgHG9su0l5ESEnFWQr5xUMj/vPwwhSYm+YP5ucx5Nezu
BM4d
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGQAgEBBCi0aeKsFfr17MTtr3IgFByp4IybuFzlgAbwPjf3rK0Fr/b87+WicOU6
oAsGCSskAwMCCAEBCqFUA1IABDQHDepa3l/S8Gt9WrNCNpCPZNBXvmkGPnVXZchZ
I5BtUySwYxHX1tpatGs3jY7drVYm+NyxZE81pecYTvXR8bu7e3BIp2SwZmXEDxdY
p1fw
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGoAgEBBDArrLo3J0sxM2YuEM0/JMMoR6ZNZNS4wZ8FDETP6VAoCrP9Kji4D59D
Oiv+3y2j66KgCwYJKyQDAwIIAQELoWQDYgAEaK8X3KCyRDMpbACw2xG4UUe9Oxyu
GWFaGKPxhKJDyW5Z56gT5P1Q2y4CblL/X9VcDIMXdcQqRNBkPQfy1+fJXwKO0Clf
D6MIE3bv6PTZ55J6H2H1dpg38a2soRchz0FN
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGoAgEBBDAEZRoS9O2fGbx85aPZzZxfj33Wu8aZ8a+K5ZZBFuVKKL+/0UbidDhD
YiuAHL6GAr2gCwYJKyQDAwIIAQEMoWQDYgAEQL0dQoOTArIx70V/XxipoxxBeKT7
zmIe7id5pQiw4O4nA2S2BFxQF9eW9ipnm6DaN6jaX/+2k+cC4qIfqzeLcLUFXxz0
qdec8lNNtr9QmwoQlv11beeHmQu9C1GwHmvG
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,7 @@
-----BEGIN EC PRIVATE KEY-----
MIHaAgEBBECYRA0UwYgMuiMq12DxXvVZ1KzsWIGQToF+3bn7JjyiG6CT+7xop4go
1W1KKsyBCEYUnL8EgGS8zpXUl+euCWSOoAsGCSskAwMCCAEBDaGBhQOBggAEYbjT
nA0x42NdM7jVv7jAoZq0iOYopbwejlOEsx8/MqRaYt4Ef83holIsgOHWSeW+kw1o
MDmieoCrhnkM/3KgGzV+BxCeieAWGxABsj9YhAmbATorRJ4q/pMxRq8gIUv05/dG
uUttl1gdbKKnGQjxDBM4v5H+/4z00nzzj4Gbfx8=
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,7 @@
-----BEGIN EC PRIVATE KEY-----
MIHaAgEBBEBNqbJnTzwGwg9JyyXBrQjXF8LT/q53Jydyv6L9AgU5c2vg5CJVvgoL
8JL/YU+i1UwVIdl8JLZuCLpi+Dcy1rlIoAsGCSskAwMCCAEBDqGBhQOBggAEm/uI
qsytMZypsqCuL0jwZh8xCRVEkUd02YPXcOBhMzS9bhAao4CLAuXhWzplr5qk/7tt
vczl7qFDOvBzNAIieZHwbFrouZ6Pew8pQXRcMDB+FnXwNgpljTNmz/f1ePjVKU1Z
gbQ+xVf8Qt8OI9S0Pla8siTgbVweGMLtq0A8Wuo=
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,8 @@
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,3F26BBC4C7A6F3B5B3A5C03C7CC59B33
rp4qC+n+qIG+WKJp4BHQHk5z0oraaaLZvoVK5glESGx5IcR3mCsN7tdg2aZ7yEk+
HnT4nQuM3R5pv248cmK0xDUje8N7FLe8lixVnEyQx3JdZfGdauowt9yaxL3AJypX
idWxNrxz1xff5RSMI6+PFv2SQpG0l794EpOjZxOUABM=
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEILnPbqUzEJ/G/paAMenPKttHR2AX91fd++CN5CSA5gtUoAoGCCqGSM49
AwEHoUQDQgAEZy7jsS+5+xqtds3fb6tp3gL+j2HNYVujNoCSkm3xWBobXt/Nozid
k4MGk0C/yeSD0n7iSpJgDwSJfAnSSlpcAA==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MGgCAQEEHIMCAGHpQ5CpglI+iI1asrSsDStMwvDwl0IS94GgBwYFK4EEACGhPAM6
AAQAHCOWAFX08+kx+jx+jrJZF1P6xvVxR0SGht35X8P+LnxxakOp3gICl68la2QZ
vCFTgn2Y3RpiuQ==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIDA00HY8HWvgF6YYE3GNc9cCxlHqL6gmeNXQjyrg7HCGoAcGBSuBBAAK
oUQDQgAEwTINsajIo/W+G+UG2iAlGPjwl4HcCLix2q7rRmCcNO0Px/dddeFdgKqH
HLWuyAKpRzn+BxuX8W3TqZnJONcYHw==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEILoEofWgFpdy7y8VmPl31ZVoI0hRFwOStu2PpKlzpgdMoAoGCCqGSM49
AwEHoUQDQgAEgr3Herakdcrvfr71ncnniKEH2Te6kcfhkHT62MzoL+kveMsY6NDu
rd7NNt9Px1scfCzkpZZI3fe4m1lHMatQMw==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDBtqXb+BJ63n8LaSU1UU25vFg8yW998I+yJwXLCgqMuPDaxfY1py4KF
mBX2kRNlBVygBwYFK4EEACKhZANiAASYI+WvOkNIX6vPYCBx57uUpFeEsK+9jbRG
FGaqN0ip/aPMWLp3n6JlmO8Ug3xgk3qvy+gJdFBJsIWs/LiCJc/sEilUlg7JAd3J
vFB22r5EORPqSGgHhdlBUM+9z8L8v2k=
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,7 @@
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIBbhPg/ddqjdlQ/u0GlA1O6bzbOaKVRn/fxzWVEOySNPMLwiOTddzy
vfuKFliNFLTx0KV4523h5UQjFB6Kwh4pDuegBwYFK4EEACOhgYkDgYYABAAHrlzH
UouimWUmKMrMBYlhLNQUzn0FahYNtOt8XxLhTUoo7ySLL4YvwZKYBp3ZMjqHyG+S
Hcy9pkkQsF3vUP9rJAGzxg/TJUEQNd6k4AQS3qtLxA2p7Ygd2zU1Ed+OM/aaj9SD
YxRQKIAqe6jZ2M71zy2oa/WZl+MNvgfb+Oq8YaPZqg==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="servletContainerFactory"
class="org.springframework.boot.web.server.servlet.MockServletWebServerFactory" />
<bean name="servlet" class="org.springframework.boot.web.servlet.mock.MockServlet"/>
</beans>

View File

@@ -0,0 +1,32 @@
-----BEGIN TRUSTED CERTIFICATE-----
MIIClzCCAgACCQCPbjkRoMVEQDANBgkqhkiG9w0BAQUFADCBjzELMAkGA1UEBhMC
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x
DTALBgNVBAoMBFRlc3QxDTALBgNVBAsMBFRlc3QxFDASBgNVBAMMC2V4YW1wbGUu
Y29tMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMB4XDTIwMDMyNzIx
NTgwNFoXDTIxMDMyNzIxNTgwNFowgY8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD
YWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKDARUZXN0
MQ0wCwYDVQQLDARUZXN0MRQwEgYDVQQDDAtleGFtcGxlLmNvbTEfMB0GCSqGSIb3
DQEJARYQdGVzdEBleGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
gYEA1YzixWEoyzrd20C2R1gjyPCoPfFLlG6UYTyT0tueNy6yjv6qbJ8lcZg7616O
3I9LuOHhZh9U+fCDCgPfiDdyJfDEW/P+dsOMFyMUXPrJPze2yPpOnvV8iJ5DM93u
fEVhCCyzLdYu0P2P3hU2W+T3/Im9DA7FOPA2vF1SrIJ2qtUCAwEAATANBgkqhkiG
9w0BAQUFAAOBgQBdShkwUv78vkn1jAdtfbB+7mpV9tufVdo29j7pmotTCz3ny5fc
zLEfeu6JPugAR71JYbc2CqGrMneSk1zT91EH6ohIz8OR5VNvzB7N7q65Ci7OFMPl
ly6k3rHpMCBtHoyNFhNVfPLxGJ9VlWFKLgIAbCmL4OIQm1l6Fr1MSM38Zw==
-----END TRUSTED CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICjzCCAfgCAQEwDQYJKoZIhvcNAQEFBQAwgY8xCzAJBgNVBAYTAlVTMRMwEQYD
VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQK
DARUZXN0MQ0wCwYDVQQLDARUZXN0MRQwEgYDVQQDDAtleGFtcGxlLmNvbTEfMB0G
CSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTAeFw0yMDAzMjcyMjAxNDZaFw0y
MTAzMjcyMjAxNDZaMIGPMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5p
YTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwEVGVzdDENMAsGA1UE
CwwEVGVzdDEUMBIGA1UEAwwLZXhhbXBsZS5jb20xHzAdBgkqhkiG9w0BCQEWEHRl
c3RAZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM7kd2cj
F49wm1+OQ7Q5GE96cXueWNPr/Nwei71tf6G4BmE0B+suXHEvnLpHTj9pdX/ZzBIK
8jIZ/x8RnSduK/Ky+zm1QMYUWZtWCAgCW8WzgB69Cn/hQG8KSX3S9bqODuQAvP54
GQJD7+4kVuNBGjFb4DaD4nvMmPtALSZf8ZCZAgMBAAEwDQYJKoZIhvcNAQEFBQAD
gYEAOn6X8+0VVlDjF+TvTgI0KIasA6nDm+KXe7LVtfvqWqQZH4qyd2uiwcDM3Aux
a/OsPdOw0j+NqFDBd3mSMhSVgfvXdK6j9WaxY1VGXyaidLARgvn63wfzgr857sQW
c8eSxbwEQxwlMvVxW6Os4VhCfUQr8VrBrvPa2zs+6IlK+Ug=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDqzCCApOgAwIBAgIIFMqbpqvipw0wDQYJKoZIhvcNAQELBQAwbDELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVBhbG8gQWx0bzEP
MA0GA1UEChMGVk13YXJlMQ8wDQYDVQQLEwZTcHJpbmcxEjAQBgNVBAMTCWxvY2Fs
aG9zdDAgFw0yMzA1MDUxMTI2NThaGA8yMTIzMDQxMTExMjY1OFowbDELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVBhbG8gQWx0bzEP
MA0GA1UEChMGVk13YXJlMQ8wDQYDVQQLEwZTcHJpbmcxEjAQBgNVBAMTCWxvY2Fs
aG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPwHWxoE3xjRmNdD
+m+e/aFlr5wEGQUdWSDD613OB1w7kqO/audEp3c6HxDB3GPcEL0amJwXgY6CQMYu
sythuZX/EZSc2HdilTBu/5T+mbdWe5JkKThpiA0RYeucQfKuB7zv4ypioa4wiR4D
nPsZXjg95OF8pCzYEssv8wT49v+M3ohWUgfF0FPlMFCSo0YVTuzB1mhDlWKq/jhQ
11WpTmk/dQX+l6ts6bYIcJt4uItG+a68a4FutuSjZdTAE0f5SOYRBpGH96mjLwEP
fW8ZjzvKb9g4R2kiuoPxvCDs1Y/8V2yvKqLyn5Tx9x/DjFmOi0DRK/TgELvNceCb
UDJmhXMCAwEAAaNPME0wHQYDVR0OBBYEFMBIGU1nwix5RS3O5hGLLoMdR1+NMCwG
A1UdEQQlMCOCCWxvY2FsaG9zdIcQAAAAAAAAAAAAAAAAAAAAAYcEfwAAATANBgkq
hkiG9w0BAQsFAAOCAQEAhepfJgTFvqSccsT97XdAZfvB0noQx5NSynRV8NWmeOld
hHP6Fzj6xCxHSYvlUfmX8fVP9EOAuChgcbbuTIVJBu60rnDT21oOOnp8FvNonCV6
gJ89sCL7wZ77dw2RKIeUFjXXEV3QJhx2wCOVmLxnJspDoKFIEVjfLyiPXKxqe/6b
dG8zzWDZ6z+M2JNCtVoOGpljpHqMPCmbDktncv6H3dDTZ83bmLj1nbpOU587gAJ8
fl1PiUDyPRIl2cnOJd+wCHKsyym/FL7yzk0OSEZ81I92LpGd/0b2Ld3m/bpe+C4Z
ILzLXTnC6AhrLcDc9QN/EO+BiCL52n7EplNLtSn1LQ==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,59 @@
Bag Attributes
friendlyName: test-alias
localKeyID: 54 69 6D 65 20 31 36 38 33 32 38 36 31 31 34 30 37 31
Key Attributes: <No Attributes>
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD8B1saBN8Y0ZjX
Q/pvnv2hZa+cBBkFHVkgw+tdzgdcO5Kjv2rnRKd3Oh8Qwdxj3BC9GpicF4GOgkDG
LrMrYbmV/xGUnNh3YpUwbv+U/pm3VnuSZCk4aYgNEWHrnEHyrge87+MqYqGuMIke
A5z7GV44PeThfKQs2BLLL/ME+Pb/jN6IVlIHxdBT5TBQkqNGFU7swdZoQ5Viqv44
UNdVqU5pP3UF/perbOm2CHCbeLiLRvmuvGuBbrbko2XUwBNH+UjmEQaRh/epoy8B
D31vGY87ym/YOEdpIrqD8bwg7NWP/Fdsryqi8p+U8fcfw4xZjotA0Sv04BC7zXHg
m1AyZoVzAgMBAAECggEAfEqiZqANaF+BqXQIb4Dw42ZTJzWsIyYYnPySOGZRoe5t
QJ03uwtULYv34xtANe1DQgd6SMyc46ugBzzjtprQ3ET5Jhn99U6kdcjf+dpf85dO
hOEppP0CkDNI39nleinSfh6uIOqYgt/D143/nqQhn8oCdSOzkbwT9KnWh1bC9T7I
vFjGfElvt1/xl88qYgrWgYLgXaencNGgiv/4/M0FNhiHEGsVC7SCu6kapC/WIQpE
5IdV+HR+tiLoGZhXlhqorY7QC4xKC4wwafVSiFxqDOQAuK+SMD4TCEv0Aop+c+SE
YBigVTmgVeJkjK7IkTEhKkAEFmRF5/5w+bZD9FhTNQKBgQD+4fNG1ChSU8RdizZT
5dPlDyAxpETSCEXFFVGtPPh2j93HDWn7XugNyjn5FylTH507QlabC+5wZqltdIjK
GRB5MIinQ9/nR2fuwGc9s+0BiSEwNOUB1MWm7wWL/JUIiKq6sTi6sJIfsYg79zco
qxl5WE94aoINx9Utq1cdWhwJTQKBgQD9IjPksd4Jprz8zMrGLzR8k1gqHyhv24qY
EJ7jiHKKAP6xllTUYwh1IBSL6w2j5lfZPpIkb4Jlk2KUoX6fN81pWkBC/fTBUSIB
EHM9bL51+yKEYUbGIy/gANuRbHXsWg3sjUsFTNPN4hGTFk3w2xChCyl/f5us8Lo8
Z633SNdpvwKBgQCGyDU9XzNzVZihXtx7wS0sE7OSjKtX5cf/UCbA1V0OVUWR3SYO
J0HPCQFfF0BjFHSwwYPKuaR9C8zMdLNhK5/qdh/NU7czNi9fsZ7moh7SkRFbzJzN
OxbKD9t/CzJEMQEXeF/nWTfsSpUgILqqZtAxuuFLbAcaAnJYlCKdAumQgQKBgQCK
mqjJh68pn7gJwGUjoYNe1xtGbSsqHI9F9ovZ0MPO1v6e5M7sQJHH+Fnnxzv/y8e8
d6tz8e73iX1IHymDKv35uuZHCGF1XOR+qrA/KQUc+vcKf21OXsP/JtkTRs1HLoRD
S5aRf2DWcfvniyYARSNU2xTM8GWgi2ueWbMDHUp+ZwKBgA/swC+K+Jg5DEWm6Sau
e6y+eC6S+SoXEKkI3wf7m9aKoZo0y+jh8Gas6gratlc181pSM8O3vZG0n19b493I
apCFomMLE56zEzvyzfpsNhFhk5MBMCn0LPyzX6MiynRlGyWIj0c99fbHI3pOMufP
WgmVLTZ8uDcSW1MbdUCwFSk5
-----END PRIVATE KEY-----
Bag Attributes
friendlyName: test-alias
localKeyID: 54 69 6D 65 20 31 36 38 33 32 38 36 31 31 34 30 37 31
subject=C = US, ST = California, L = Palo Alto, O = VMware, OU = Spring, CN = localhost
issuer=C = US, ST = California, L = Palo Alto, O = VMware, OU = Spring, CN = localhost
-----BEGIN CERTIFICATE-----
MIIDqzCCApOgAwIBAgIIFMqbpqvipw0wDQYJKoZIhvcNAQELBQAwbDELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVBhbG8gQWx0bzEP
MA0GA1UEChMGVk13YXJlMQ8wDQYDVQQLEwZTcHJpbmcxEjAQBgNVBAMTCWxvY2Fs
aG9zdDAgFw0yMzA1MDUxMTI2NThaGA8yMTIzMDQxMTExMjY1OFowbDELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVBhbG8gQWx0bzEP
MA0GA1UEChMGVk13YXJlMQ8wDQYDVQQLEwZTcHJpbmcxEjAQBgNVBAMTCWxvY2Fs
aG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPwHWxoE3xjRmNdD
+m+e/aFlr5wEGQUdWSDD613OB1w7kqO/audEp3c6HxDB3GPcEL0amJwXgY6CQMYu
sythuZX/EZSc2HdilTBu/5T+mbdWe5JkKThpiA0RYeucQfKuB7zv4ypioa4wiR4D
nPsZXjg95OF8pCzYEssv8wT49v+M3ohWUgfF0FPlMFCSo0YVTuzB1mhDlWKq/jhQ
11WpTmk/dQX+l6ts6bYIcJt4uItG+a68a4FutuSjZdTAE0f5SOYRBpGH96mjLwEP
fW8ZjzvKb9g4R2kiuoPxvCDs1Y/8V2yvKqLyn5Tx9x/DjFmOi0DRK/TgELvNceCb
UDJmhXMCAwEAAaNPME0wHQYDVR0OBBYEFMBIGU1nwix5RS3O5hGLLoMdR1+NMCwG
A1UdEQQlMCOCCWxvY2FsaG9zdIcQAAAAAAAAAAAAAAAAAAAAAYcEfwAAATANBgkq
hkiG9w0BAQsFAAOCAQEAhepfJgTFvqSccsT97XdAZfvB0noQx5NSynRV8NWmeOld
hHP6Fzj6xCxHSYvlUfmX8fVP9EOAuChgcbbuTIVJBu60rnDT21oOOnp8FvNonCV6
gJ89sCL7wZ77dw2RKIeUFjXXEV3QJhx2wCOVmLxnJspDoKFIEVjfLyiPXKxqe/6b
dG8zzWDZ6z+M2JNCtVoOGpljpHqMPCmbDktncv6H3dDTZ83bmLj1nbpOU587gAJ8
fl1PiUDyPRIl2cnOJd+wCHKsyym/FL7yzk0OSEZ81I92LpGd/0b2Ld3m/bpe+C4Z
ILzLXTnC6AhrLcDc9QN/EO+BiCL52n7EplNLtSn1LQ==
-----END CERTIFICATE-----