Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@ServletComponentScan does not register servlet components in a mock web environment #39736

Closed
Crain-32 opened this issue Feb 22, 2024 · 6 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@Crain-32
Copy link

Affects: 2.7.9 - 3.2.3 (Likely more, I just tested the latest and a random 2.7)

When using @WebFilter with either the value or urlPatterns fields defined, Spring fails to respect them. It would make sense that the following behaves the same.

@Webfilter("/rest/example/*")
@Component
public static class SimpleFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        filterChain.doFilter(request, response);
    }
}
// Should behavior similar to
@Webfilter
@Component
public static class SimpleFilter extends OncePerRequestFilter {
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        return !request.getRequestURI().startsWith("/rest/example");
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        filterChain.doFilter(request, response);
    }
}

A repository configured with all three WebFilter variations and tests for them can be found here.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 22, 2024
@snicoll
Copy link
Member

snicoll commented Feb 23, 2024

Thanks for the report but that's the wrong issue tracker. Moving to Spring Boot.

From a quick debugging session, those filters are indeed taken into account by SpringBootMockMvcBuilderCustomizer but their urlPatterns collection is empty.

@snicoll snicoll transferred this issue from spring-projects/spring-framework Feb 23, 2024
@snicoll snicoll changed the title WebFilter Annotation should be respected Filter registered with @WebFilter are not registered against MockMvc Feb 23, 2024
@wilkinsona
Copy link
Member

Mixing Spring's component model and the Servlet spec's component model isn't supported. This means that your sample also doesn't work as you would like when running its main method as the url patterns are ignored there too.

If you want to use @WebFilter you should not use @Component. Instead, you should enable scanning for servlet components using @ServletComponentScan:

@ServletComponentScan
@SpringBootApplication
public class WebfilterBugApplication {

    public static void main(String[] args) {
        SpringApplication.run(WebfilterBugApplication.class, args);
    }

    @WebFilter("/not/simple")
    public static class SimpleFilterOne extends OncePerRequestFilter {

        public static boolean CALLED = false;
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        	System.out.println("One");
            CALLED = true;
            filterChain.doFilter(request, response);
        }
    }

    @WebFilter
    public static class SimpleFilterTwo extends OncePerRequestFilter {

        public static boolean CALLED = false;
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        	System.out.println("Two");
            CALLED = true;
            filterChain.doFilter(request, response);
        }
    }

    @WebFilter(urlPatterns = "/not/simple")
    public static class SimpleFilterThree extends OncePerRequestFilter {

        public static boolean CALLED = false;
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        	System.out.println("Three");        	
            CALLED = true;
            filterChain.doFilter(request, response);
        }
    }

    @RestController
    @RequestMapping("/rest/simple")
    public static class SimpleController {


        @GetMapping
        public String getString() {
            return "Hello World";
        }
    }
}

This will result in the @WebFilters being found and their attributes being honored when it's started using its main method. Unfortunately, it doesn't fix the tests as I have just discovered that the registration of servlet components doesn't work in a mock web environment. It works with @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) but not with the default mock environment. We can use this issue to fix that.

@wilkinsona wilkinsona changed the title Filter registered with @WebFilter are not registered against MockMvc @ServletComponentScan does not register servlet components in a mock web environment Feb 23, 2024
@wilkinsona wilkinsona added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Feb 23, 2024
@wilkinsona wilkinsona added this to the 3.1.x milestone Feb 23, 2024
@askneller
Copy link

@wilkinsona I'd like to take at look at this if that's ok.

@askneller
Copy link

According to the ServletComponentScan docs, scanning is only performed when using an embedded server.

* Enables scanning for Servlet components ({@link WebFilter filters}, {@link WebServlet
* servlets}, and {@link WebListener listeners}). Scanning is only performed when using an
* embedded web server.
* <p>

And according to the MOCK WebEnvironment it is not using an embedded server.

enum WebEnvironment {
/**
* Creates a {@link WebApplicationContext} with a mock servlet environment if
* servlet APIs are on the classpath, a {@link ReactiveWebApplicationContext} if
* Spring WebFlux is on the classpath or a regular {@link ApplicationContext}
* otherwise.
*/
MOCK(false),
/**
* Creates a web application context (reactive or servlet based) and sets a
* {@code server.port=0} {@link Environment} property (which usually triggers
* listening on a random port). Often used in conjunction with a
* {@link LocalServerPort @LocalServerPort} injected field on the test.
*/
RANDOM_PORT(true),
/**
* Creates a (reactive) web application context without defining any
* {@code server.port=0} {@link Environment} property.
*/
DEFINED_PORT(true),
/**
* Creates an {@link ApplicationContext} and sets
* {@link SpringApplication#setWebApplicationType(WebApplicationType)} to
* {@link WebApplicationType#NONE}.
*/
NONE(false);
private final boolean embedded;
WebEnvironment(boolean embedded) {
this.embedded = embedded;
}
/**
* Return if the environment uses an {@link ServletWebServerApplicationContext}.
* @return if an {@link ServletWebServerApplicationContext} is used.
*/
public boolean isEmbedded() {
return this.embedded;
}
}

So that at least explains why the WebFilters are not picked up. Not sure if you still want the filters to be excluded from the MOCK environment.

@wilkinsona
Copy link
Member

Thanks for taking a look, @askneller. What you have described is this crux of the problem and I'm not yet sure how best to fix it. I think it's reasonable to expect @ServletComponentScan to work with a mock web environment, but that expectation does contradict the current documentation. I think the core team will have to spend some time discussing this one before we can move forwards.

@wilkinsona wilkinsona added the for: team-meeting An issue we'd like to discuss as a team to make progress label Mar 19, 2024
@philwebb philwebb removed the for: team-meeting An issue we'd like to discuss as a team to make progress label Mar 20, 2024
@philwebb
Copy link
Member

I think the embedded boolean in WebEnvironment isn't very well named. It's designed to trigger the creation of a ServletWebServerApplicationContext which we don't want for mock environments.

I think we'll need to fix this at the @ServletComponentScan level.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

6 participants