Home > Blockchain >  Spring Actuator returns 404 with Spring Boot (war project)
Spring Actuator returns 404 with Spring Boot (war project)

Time:06-03

I'm using Spring Boot 2.7.0 (but I've also tried 2.6.7 and 2.5) with Java 17, and I want to add actuator to the project. The project is packaged as a war because it's still using JSP's, which we haven't gotten to work when packing it as jar. It uses a very old version of spring-security (4.2.18.RELEASE, still using XML configuration) because of compatibility issues, but other than that, dependencies should be up-to-date. In other projects, I've never had any problems with actuator. Anyways I've added this to the pom.xml file:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

With these properties:

management.server.port=8081
management.endpoint.health.enabled=true
management.endpoint.health.show-details=always
management.endpoint.health.status.http-mapping.DOWN=200
management.endpoints.web.exposure.include=*

We've added this setting to our spring security XML configuration to allow all calls to actuator:

<http pattern="/actuator/**" auto-config="true" use-expressions="true" create-session="stateless" disable-url-rewriting="true" security="none"/>

We have (a lot) of custom configuration, but the relevant parts that I can think of, are defined like this:

@EnableRabbit
@EnableRetry
@SpringBootApplication
@EnableAsync
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan({"com.company.something", "com.company.another"})
@ImportResource(locations = {"classpath*:META-INF/spring/applicationContext*.xml", "classpath:spring/webflow-config.xml"})
public class Bootstrap {

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

and

@Configuration
public class WebappConfiguration implements WebMvcConfigurer {

    @Bean
    public TomcatServletWebServerFactory tomcatFactory() {
        return new CustomTomcatServletWebServerFactory();
    }

    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> servletContainerCustomizer() {
        return container -> {
            container.addContextCustomizers(ctx -> ctx.setReloadable(false));
            container.addConnectorCustomizers(con -> con.setMaxPostSize(5000000));
        };
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/web-resources/**").addResourceLocations("classpath:/META-INF/web-resources/");
    }


    @Bean
    public DispatcherServletRegistrationBean dispatcherServletRegistrationBean() {
        DispatcherServlet servlet = new DispatcherServlet();
        servlet.setApplicationContext(new AnnotationConfigWebApplicationContext());
        return new DispatcherServletRegistrationBean(servlet, "/");
    }

    private static class CustomTomcatServletWebServerFactory extends TomcatServletWebServerFactory {
        @Override
        protected void postProcessContext(Context context) {
            ((StandardJarScanner) context.getJarScanner()).setScanManifest(false);
            context.setResources(new ExtractingRoot());
        }
    }
}

When I run the application I get this error:

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 2 of method servletEndpointRegistrar in org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration required a single bean, but 2 were found:
    - dispatcherServletRegistration: a programmatically registered singleton    - dispatcherServletRegistrationBean: defined by method 'dispatcherServletRegistrationBean' in class path resource [com/company/WebappConfiguration.class]

This is the first thing that I find pretty weird. From what I understand, Spring seems to detect two servletEndpointRegistrar beans. If I try to remove the dispatcherServletRegistrationBean I run into this error:

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of method errorPageCustomizer in org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration required a bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath' that could not be found.

If I instead add the @Primary annotation to dispatcherServletRegistrationBean:

@Primary
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean() {
    DispatcherServlet servlet = new DispatcherServlet();
    servlet.setApplicationContext(new AnnotationConfigWebApplicationContext());
    return new DispatcherServletRegistrationBean(servlet, "/");
}

the application does indeed start. But if I navigate to http://localhost:8081/actuator all I get is this:

<Map>
  <timestamp>2022-05-24T06:38:58.914 00:00</timestamp>
  <status>404</status>
  <error>Not Found</error>
  <message>No message available</message>
  <path>/actuator</path>
</Map>

What could be the cause of this and how can I solve it?

CodePudding user response:

I managed to solve this by creating the DispatchServlet as a separate bean from the DispatcherServletRegistrationBean:

@Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
    DispatcherServlet servlet = new DispatcherServlet();
    servlet.setApplicationContext(new AnnotationConfigWebApplicationContext());
    return servlet;
}

@Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
    return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
  • Related