When using Spring boot with actuator, jersey configured with filter type and component extends from ResourceConfig, start failed
It's ok with servlet type or without actuator deps
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jerseyWebEndpointsResourcesRegistrar' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration$JerseyWebEndpointsResourcesRegistrar]: Factory method 'jerseyWebEndpointsResourcesRegistrar' threw exception; nested exception is java.lang.IllegalStateException: The resource configuration is not modifiable in this context.
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658) ~[spring-beans-5.3.3.jar:5.3.3]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:638) ~[spring-beans-5.3.3.jar:5.3.3]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) ~[spring-beans-5.3.3.jar:5.3.3]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1179) ~[spring-beans-5.3.3.jar:5.3.3]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:571) ~[spring-beans-5.3.3.jar:5.3.3]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:531) ~[spring-beans-5.3.3.jar:5.3.3]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.3.jar:5.3.3]
at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$161/1765795529.getObject(Unknown Source) ~[na:na]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.3.jar:5.3.3]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.3.jar:5.3.3]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.3.jar:5.3.3]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.3.jar:5.3.3]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:923) ~[spring-context-5.3.3.jar:5.3.3]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:588) ~[spring-context-5.3.3.jar:5.3.3]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:144) ~[spring-boot-2.4.2.jar:2.4.2]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:767) [spring-boot-2.4.2.jar:2.4.2]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) [spring-boot-2.4.2.jar:2.4.2]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426) [spring-boot-2.4.2.jar:2.4.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:326) [spring-boot-2.4.2.jar:2.4.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1311) [spring-boot-2.4.2.jar:2.4.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) [spring-boot-2.4.2.jar:2.4.2]
at com.example.demo.DemoApplication.main(DemoApplication.java:10) [main/:na]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration$JerseyWebEndpointsResourcesRegistrar]: Factory method 'jerseyWebEndpointsResourcesRegistrar' threw exception; nested exception is java.lang.IllegalStateException: The resource configuration is not modifiable in this context.
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.3.jar:5.3.3]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.3.jar:5.3.3]
... 21 common frames omitted
Caused by: java.lang.IllegalStateException: The resource configuration is not modifiable in this context.
at org.glassfish.jersey.server.ResourceConfig$ImmutableState.registerResources(ResourceConfig.java:208) ~[jersey-server-2.32.jar:na]
at org.glassfish.jersey.server.ResourceConfig.registerResources(ResourceConfig.java:576) ~[jersey-server-2.32.jar:na]
at org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration$JerseyWebEndpointsResourcesRegistrar.register(JerseyWebEndpointManagementContextConfiguration.java:141) ~[spring-boot-actuator-autoconfigure-2.4.2.jar:2.4.2]
at org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration$JerseyWebEndpointsResourcesRegistrar.register(JerseyWebEndpointManagementContextConfiguration.java:128) ~[spring-boot-actuator-autoconfigure-2.4.2.jar:2.4.2]
at org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration$JerseyWebEndpointsResourcesRegistrar.<init>(JerseyWebEndpointManagementContextConfiguration.java:113) ~[spring-boot-actuator-autoconfigure-2.4.2.jar:2.4.2]
at org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration.jerseyWebEndpointsResourcesRegistrar(JerseyWebEndpointManagementContextConfiguration.java:76) ~[spring-boot-actuator-autoconfigure-2.4.2.jar:2.4.2]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_25]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_25]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_25]
at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_25]
at org.springframework.beans.fac
To reproduce :
- https://start.spring.io/#!type=gradle-project&language=java&platformVersion=2.4.2.RELEASE&packaging=jar&jvmVersion=15&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo&dependencies=lombok,configuration-processor,devtools,jersey,actuator
- application.properties
spring.jersey.type=filter
- define JerseyConfig
package com.example.demo;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;
@Component
public class JerseyConfig extends ResourceConfig {
}
** your app must not have spring-web-mvc deps include on runtime" like spring-cloud-starter-netflix-hystrix-dashboard or org.springframework.boot:spring-boot-starter-web or bean "org.springframework.web.servlet.DispatcherServlet"
seems to be ko from long time ago, try with 2.3.x and 2.2.x with same bug
https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-jersey
Comment From: wilkinsona
The problem's occurring because filters are initialised more eagerly than servlets. When the initialization happens, Jersey's state is made immutable. If this happens before a call to registerResources
is made, the call will fail. The problem does not occur with a servlet even when it's configured to be loaded on startup as this is still late enough.
@Fyro-Ing You can work around the problem by configuring Jersey's filter to be initialised lazily using Boot's DelegatingFilterProxyRegistrationBean
. You can do so by adding a couple of beans to your application. The following beans mimic Boot's auto-configuration but also cause the filter to be initialised lazily:
@Bean
public DelegatingFilterProxyRegistrationBean jerseyFilterRegistration(JerseyApplicationPath applicationPath,
ResourceConfig resourceConfig, JerseyProperties jersey) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean("jerseyFilter") {
@Override
public DelegatingFilterProxy getFilter() {
DelegatingFilterProxy filter = super.getFilter();
filter.setTargetFilterLifecycle(true);
return filter;
}
};
registration.setUrlPatterns(Collections.singletonList(applicationPath.getUrlMapping()));
registration.setOrder(jersey.getFilter().getOrder());
registration.addInitParameter(ServletProperties.FILTER_CONTEXT_PATH, stripPattern(applicationPath.getPath()));
addInitParameters(jersey.getInit(), registration);
registration.setName("jerseyFilter");
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
return registration;
}
@Bean
public ServletContainer jerseyFilter(ResourceConfig resourceConfig) {
return new ServletContainer(resourceConfig);
}
private String stripPattern(String path) {
if (path.endsWith("/*")) {
path = path.substring(0, path.lastIndexOf("/*"));
}
return path;
}
private void addInitParameters(Map<String, String> initParameters, DynamicRegistrationBean<?> registration) {
initParameters.forEach(registration::addInitParameter);
}
Comment From: wilkinsona
The last version where this worked appears to be 2.1.8.RELEASE as it breaks in 2.1.9.RELEASE. I think that's due to the changes made for https://github.com/spring-projects/spring-boot/issues/17801 which stopped using a ResourceConfigCustomizer
to register the endpoints. Using a ResourceConfigCustomizer
ensured that the ResourceConfig
was configured before it was locked, irrespective of the differing lifecycles of servlets and filters. An alternative to the delegating filter proxy approach shown above may be to rework the fix for #17801 somehow so that we can go back to using a ResourceConfigCustomizer
again.
Comment From: philwebb
We'll update our auto-config to use the DelegatingFilterProxyRegistrationBean
.
Comment From: wilkinsona
A downside of the delegating filter proxy approach that we didn't consider is that many Jersey-related errors won't be discovered until the first request is received. That doesn't feel great from a usability perspective. I think it would be good if we can rework the fix for #17801 to use a ResourceConfigCustomizer
.
Comment From: wilkinsona
Here's a fix that avoids the use of a delegating filter proxy. Instead, it reworks the fix for #17801 by introducing a ManagementContextResourceConfigCustomizer
that can be used to customize the ResourceConfig
that the Actuator is using without also applying the main application's customisers to it. Flagging for team attention to see if we're comfortable with the new public API that this requires.