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

Application fails to start when using Actuator and Jersey configured as a Filter #25262

Closed
Fyro-Ing opened this issue Feb 12, 2021 · 5 comments
Closed
Assignees
Labels
type: bug A general bug
Milestone

Comments

@Fyro-Ing
Copy link

Fyro-Ing commented Feb 12, 2021

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 :

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

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 12, 2021
@Fyro-Ing Fyro-Ing changed the title actuator and jersey with filter type and ResourceConfig failed actuator and jersey with filter type and ResourceConfig result to run failed Feb 12, 2021
@wilkinsona
Copy link
Member

wilkinsona commented Feb 12, 2021

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);
}

@wilkinsona wilkinsona added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Feb 12, 2021
@wilkinsona wilkinsona added this to the 2.3.x milestone Feb 12, 2021
@wilkinsona
Copy link
Member

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 #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.

@wilkinsona wilkinsona added the for: team-meeting An issue we'd like to discuss as a team to make progress label Feb 16, 2021
@philwebb
Copy link
Member

We'll update our auto-config to use the DelegatingFilterProxyRegistrationBean.

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

wilkinsona commented Feb 23, 2021

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.

@wilkinsona
Copy link
Member

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.

@wilkinsona wilkinsona added the for: team-attention An issue we'd like other members of the team to review label Feb 24, 2021
@philwebb philwebb removed the for: team-attention An issue we'd like other members of the team to review label Feb 24, 2021
@wilkinsona wilkinsona changed the title actuator and jersey with filter type and ResourceConfig result to run failed Application fails to start when using Actuator and Jersey configured as a Filter Feb 26, 2021
@wilkinsona wilkinsona self-assigned this Feb 26, 2021
@wilkinsona wilkinsona modified the milestones: 2.3.x, 2.3.10 Feb 26, 2021
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

4 participants