How do i prevent a spring boot webapplication from accepting http requests while it is still starting up?

Viewed 642

I have a spring boot webapplication with a angular frontend. When i send a request using the angular app while the webapplication is still starting up it may occur that the webapplication can accept that request and will try processing it. I believe the application is capable of receiving requests the moment that the WebSecurityConfigurerAdapter extending class has been initialized.

This causes problems when the rest of the classes have yet to fully initialize. For instance, when i try to log out a user that is still logged in while the webapplication is starting up, it may cause a SQLException because it can't find the database table user yet.

How do i prevent the application from accepting requests until the final class (the one annotated with @SpringBootApplication) has been initialized?

2 Answers

Based on the hint of @Fullslack.dev i've come up with the following solution.

@WebFilter(urlPatterns = {"/*"})
public class MyFilter implements Filter, ApplicationListener<ApplicationReadyEvent> {

    private boolean webApplicationReadyToAcceptRequests = false;

    @Override 
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        webApplicationReadyToAcceptRequests = true;
    }
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
            if(!webApplicationReadyToAcceptRequests) {
                httpServletResponse.setHeader("Retry-After","35");
                httpServletResponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "The server is currently starting up. Please try again in a moment.");
                return;
            }
            else
                filterChain.doFilter(servletRequest, servletResponse);
            }
    }

}

This is a filter class where every request must pass through first before it is presented to the @Controller and/or @Service annotated classes. If the variable webApplicationReadyToAcceptRequests is still false then the filter won't pass on the request downstream, instead it will send an error message response with httpstatus 503 (service unavailable).

Make sure you annotate atleast one of your @Configuration config classes with this:

@ComponentScan(basePackages={ "FolderWhereMyFilterIsLocated"})

This will make sure that spring is aware of the existence of the filter.

Approach with ApplicationReadyEvent is not good enough as it gets propagated after Spring web application has started up and web server is able to accept requests by that time:

Tomcat started on port(s): 4134 (http) with context path ''

and application is considered up and running:

http://localhost:4134/actuator/health returns {"status":"UP"}

If you need to do some init before that then you have to attach that to spring context initialization, similarly to how it's done for DB connections. The simplest way is to have your "init" code in some spring bean's @PostConstruct method. Alternatively there is an old-school InitializingBean interface with a similar effect in afterPropertiesSet() method.

Related