diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockFilterRegistration.java b/spring-test/src/main/java/org/springframework/mock/web/MockFilterRegistration.java new file mode 100644 index 0000000000..baa704dd77 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/mock/web/MockFilterRegistration.java @@ -0,0 +1,129 @@ +/* + * Copyright 2002-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.mock.web; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import jakarta.servlet.DispatcherType; +import jakarta.servlet.FilterRegistration; + +import org.springframework.lang.Nullable; + +/** + * Mock implementation of {@link FilterRegistration}. + * + * @author Rossen Stoyanchev + * @since 6.2 + */ +public class MockFilterRegistration implements FilterRegistration { + + private final String name; + + private final String className; + + private final Map initParameters = new LinkedHashMap<>(); + + private final List servletNames = new ArrayList<>(); + + private final List urlPatterns = new ArrayList<>(); + + + public MockFilterRegistration(String className) { + this(className, ""); + } + + public MockFilterRegistration(String className, String name) { + this.name = name; + this.className = className; + } + + + @Override + public String getName() { + return this.name; + } + + @Nullable + @Override + public String getClassName() { + return this.className; + } + + @Override + public boolean setInitParameter(String name, String value) { + return (this.initParameters.putIfAbsent(name, value) != null); + } + + @Nullable + @Override + public String getInitParameter(String name) { + return this.initParameters.get(name); + } + + @Override + public Set setInitParameters(Map initParameters) { + Set existingParameterNames = new LinkedHashSet<>(); + for (Map.Entry entry : initParameters.entrySet()) { + if (this.initParameters.get(entry.getKey()) != null) { + existingParameterNames.add(entry.getKey()); + } + } + if (existingParameterNames.isEmpty()) { + this.initParameters.putAll(initParameters); + } + return existingParameterNames; + } + + @Override + public Map getInitParameters() { + return Collections.unmodifiableMap(this.initParameters); + } + + @Override + public void addMappingForServletNames( + EnumSet dispatcherTypes, boolean isMatchAfter, String... servletNames) { + + this.servletNames.addAll(Arrays.asList(servletNames)); + } + + @Override + public Collection getServletNameMappings() { + return Collections.unmodifiableCollection(this.servletNames); + } + + @Override + public void addMappingForUrlPatterns( + EnumSet dispatcherTypes, boolean isMatchAfter, String... urlPatterns) { + + this.urlPatterns.addAll(Arrays.asList(urlPatterns)); + } + + @Override + public Collection getUrlPatternMappings() { + return Collections.unmodifiableCollection(this.urlPatterns); + } + +} diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java b/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java index ce74242b50..38e7dae2ba 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java @@ -145,6 +145,8 @@ public class MockServletContext implements ServletContext { @Nullable private String responseCharacterEncoding; + private final Map filterRegistrations = new LinkedHashMap<>(); + private final Map mimeTypes = new LinkedHashMap<>(); @@ -604,6 +606,25 @@ public class MockServletContext implements ServletContext { return this.responseCharacterEncoding; } + /** + * Add a {@link FilterRegistration}. + * @since 6.2 + */ + public void addFilterRegistration(FilterRegistration registration) { + this.filterRegistrations.put(registration.getName(), registration); + } + + @Override + @Nullable + public FilterRegistration getFilterRegistration(String filterName) { + return this.filterRegistrations.get(filterName); + } + + @Override + public Map getFilterRegistrations() { + return Collections.unmodifiableMap(this.filterRegistrations); + } + //--------------------------------------------------------------------- // Unsupported Servlet 3.0 registration methods @@ -678,25 +699,6 @@ public class MockServletContext implements ServletContext { throw new UnsupportedOperationException(); } - /** - * This method always returns {@code null}. - * @see jakarta.servlet.ServletContext#getFilterRegistration(java.lang.String) - */ - @Override - @Nullable - public FilterRegistration getFilterRegistration(String filterName) { - return null; - } - - /** - * This method always returns an {@linkplain Collections#emptyMap empty map}. - * @see jakarta.servlet.ServletContext#getFilterRegistrations() - */ - @Override - public Map getFilterRegistrations() { - return Collections.emptyMap(); - } - @Override public void addListener(Class listenerClass) { throw new UnsupportedOperationException(); diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/MockMvcFilterDecorator.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/MockMvcFilterDecorator.java index 4574618628..aaa7f4e4fb 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/MockMvcFilterDecorator.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/MockMvcFilterDecorator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-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. @@ -35,6 +35,8 @@ import jakarta.servlet.http.HttpServletRequest; import org.springframework.lang.Nullable; import org.springframework.mock.web.MockFilterConfig; +import org.springframework.mock.web.MockFilterRegistration; +import org.springframework.mock.web.MockServletContext; import org.springframework.util.Assert; import org.springframework.web.util.UrlPathHelper; @@ -98,17 +100,27 @@ final class MockMvcFilterDecorator implements Filter { Assert.notNull(delegate, "filter cannot be null"); Assert.notNull(urlPatterns, "urlPatterns cannot be null"); this.delegate = delegate; - this.filterConfigInitializer = getFilterConfigInitializer(filterName, initParams); + this.filterConfigInitializer = getFilterConfigInitializer(delegate, filterName, initParams); this.dispatcherTypes = dispatcherTypes; this.hasPatterns = initPatterns(urlPatterns); } private static Function getFilterConfigInitializer( - @Nullable String filterName, @Nullable Map initParams) { + Filter delegate, @Nullable String filterName, @Nullable Map initParams) { + + String className = delegate.getClass().getName(); return servletContext -> { - MockFilterConfig filterConfig = (filterName != null ? - new MockFilterConfig(servletContext, filterName) : new MockFilterConfig(servletContext)); + MockServletContext mockServletContext = (MockServletContext) servletContext; + MockFilterConfig filterConfig; + if (filterName != null) { + filterConfig = new MockFilterConfig(servletContext, filterName); + mockServletContext.addFilterRegistration(new MockFilterRegistration(className, filterName)); + } + else { + filterConfig = new MockFilterConfig(servletContext); + mockServletContext.addFilterRegistration(new MockFilterRegistration(className)); + } if (initParams != null) { initParams.forEach(filterConfig::addInitParameter); } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilderTests.java index 854ca7ad4b..e8f7b71eca 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-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. @@ -35,6 +35,7 @@ import org.mockito.ArgumentCaptor; import org.springframework.http.converter.json.SpringHandlerInstantiator; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.stereotype.Controller; +import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; @@ -133,12 +134,17 @@ class StandaloneMockMvcBuilderTests { Filter filter = mock(Filter.class); ArgumentCaptor captor = ArgumentCaptor.forClass(FilterConfig.class); - MockMvcBuilders.standaloneSetup(new PersonController()) - .addFilter(filter, null, Map.of("p", "v"), EnumSet.of(DispatcherType.REQUEST), "/") + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new PersonController()) + .addFilter(filter, "testFilter", Map.of("p", "v"), EnumSet.of(DispatcherType.REQUEST), "/") .build(); verify(filter, times(1)).init(captor.capture()); assertThat(captor.getValue().getInitParameter("p")).isEqualTo("v"); + + // gh-33252 + + assertThat(mockMvc.getDispatcherServlet().getServletContext().getFilterRegistrations()) + .hasSize(1).containsKey("testFilter"); } @Test // SPR-13375