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, "/");
}