springfox icon indicating copy to clipboard operation
springfox copied to clipboard

Spring 5.3/Spring Boot 2.4 support

Open MiniDigger opened this issue 5 years ago • 101 comments

If you enable the new PathPatternParser (https://spring.io/blog/2020/06/30/url-matching-with-pathpattern-in-spring-mvc), springfox fails with an NPE

Details
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException: Cannot invoke "org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getPatterns()" because "this.condition" is null
	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:185) ~[spring-context-5.3.0-M1.jar:5.3.0-M1]
	at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:53) ~[spring-context-5.3.0-M1.jar:5.3.0-M1]
	at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:360) ~[spring-context-5.3.0-M1.jar:5.3.0-M1]
	at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:158) ~[spring-context-5.3.0-M1.jar:5.3.0-M1]
	at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:122) ~[spring-context-5.3.0-M1.jar:5.3.0-M1]
	at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:913) ~[spring-context-5.3.0-M1.jar:5.3.0-M1]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:570) ~[spring-context-5.3.0-M1.jar:5.3.0-M1]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-2.4.0-M1.jar:2.4.0-M1]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.4.0-M1.jar:2.4.0-M1]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) ~[spring-boot-2.4.0-M1.jar:2.4.0-M1]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.4.0-M1.jar:2.4.0-M1]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.4.0-M1.jar:2.4.0-M1]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.4.0-M1.jar:2.4.0-M1]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.4.0-M1.jar:2.4.0-M1]
	at me.minidigger.hangar.HangarApplication.main(HangarApplication.java:10) ~[classes/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
	at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-2.4.0-M1.jar:2.4.0-M1]
Caused by: java.lang.NullPointerException: Cannot invoke "org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getPatterns()" because "this.condition" is null
	at springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns(WebMvcPatternsRequestConditionWrapper.java:56) ~[springfox-spring-webmvc-3.0.0.jar:3.0.0]
	at springfox.documentation.RequestHandler.sortedPaths(RequestHandler.java:113) ~[springfox-core-3.0.0.jar:3.0.0]
	at springfox.documentation.spi.service.contexts.Orderings.lambda$byPatternsCondition$3(Orderings.java:89) ~[springfox-spi-3.0.0.jar:3.0.0]
	at java.base/java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469) ~[na:na]
	at java.base/java.util.TimSort.binarySort(TimSort.java:296) ~[na:na]
	at java.base/java.util.TimSort.sort(TimSort.java:239) ~[na:na]
	at java.base/java.util.Arrays.sort(Arrays.java:1306) ~[na:na]
	at java.base/java.util.ArrayList.sort(ArrayList.java:1720) ~[na:na]
	at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:392) ~[na:na]
	at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
	at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
	at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
	at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[na:na]
	at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:81) ~[springfox-spring-webmvc-3.0.0.jar:3.0.0]
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) ~[na:na]
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1624) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[na:na]
	at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.withDefaults(AbstractDocumentationPluginsBootstrapper.java:107) ~[springfox-spring-web-3.0.0.jar:3.0.0]
	at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.buildContext(AbstractDocumentationPluginsBootstrapper.java:91) ~[springfox-spring-web-3.0.0.jar:3.0.0]
	at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.bootstrapDocumentationPlugins(AbstractDocumentationPluginsBootstrapper.java:82) ~[springfox-spring-web-3.0.0.jar:3.0.0]
	at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:100) ~[springfox-spring-web-3.0.0.jar:3.0.0]
	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:182) ~[spring-context-5.3.0-M1.jar:5.3.0-M1]
	... 19 common frames omitted

I thought it was fixed by this line https://github.com/springfox/springfox/blob/master/springfox-spring-webmvc/src/main/java/springfox/documentation/spring/web/WebMvcRequestHandler.java#L83

-        requestMapping.getPatternsCondition());
+        requestMapping.getActivePatternsCondition());

but I couldn't get this to work in 5 minutes, this prolly needs larger refactors.

for reference: https://github.com/spring-projects/spring-framework/blob/b572f7618f01897b611a47376c538b88be4dff80/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java#L219

edit: if you face this issue, you should consider upgrading to springdoc since this lib seems dead: https://springdoc.org/migrating-from-springfox.html

MiniDigger avatar Jul 29 '20 18:07 MiniDigger

I am also running into this same issue. Is there a ETA for Spring 5.3 support?

jojijohn avatar Mar 10 '21 15:03 jojijohn

I also have the same problem。 I find this:spring boot 2.6.0-M2 is support,but spring boot 2.6.0-M3 not support spring framwork 5.3.9 support, but spring framwork 5.3.10 not support now I am find what problem cause。

and I want to know if this project not continue maintenance。it has 12 month not update。

euler-king avatar Oct 14 '21 03:10 euler-king

Are you planning on adding support for Spring Boot 2.6.0-M3?

leinadpb avatar Oct 20 '21 20:10 leinadpb

Confirming that I have encountered this issue in Spring Boot 2.6.0-M3 also.

pdavie avatar Oct 20 '21 23:10 pdavie

I also have the same problem。 I find this:spring boot 2.6.0-M2 is support,but spring boot 2.6.0-M3 not support spring framwork 5.3.9 support, but spring framwork 5.3.10 not support now I am find what problem cause。

and I want to know if this project not continue maintenance。it has 12 month not update。

I also have the same proplem. And the reason is this. Thanks!

wuyinq avatar Oct 24 '21 08:10 wuyinq

Are you planning on adding support for Spring Boot 2.6.0-M3?

I wait springfox official to fix this problem。or hope spring boot 2.6.0 release can support old version。when I have time, I alse want to support affter Spring Boot 2.6.0-M3?

euler-king avatar Oct 25 '21 05:10 euler-king

The same problem still happens with Spring Boot 2.6.0-RC1.

Sonix avatar Nov 09 '21 12:11 Sonix

Hacky workaround (don't know what that breaks, I don't use Documentation):

Remove @Component from springfox-spring-web/src/main/java/springfox/documentation/spring/web/plugins/DocumentationPluginsBootstrapper.java

My app launches afterwards and works.

Sonix avatar Nov 09 '21 14:11 Sonix

Not working with Spring Boot 2.6.0

ubaid4j avatar Nov 20 '21 02:11 ubaid4j

I find this in the Spring Boot Release-Notes, But it seems not works for me. https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.6-Release-Notes#pathpattern-based-path-matching-strategy-for-spring-mvc

lWoHvYe avatar Nov 20 '21 14:11 lWoHvYe

I find this in the Spring Boot Release-Notes, But it seems not works for me. https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.6-Release-Notes#pathpattern-based-path-matching-strategy-for-spring-mvc

yeah it didn't work

ubaid4j avatar Nov 20 '21 14:11 ubaid4j

From the perspective of debugging, the condition content of the old version has value / {???} / {???} But the new version is empty

dujie-js avatar Nov 21 '21 00:11 dujie-js

Workaround for Spring Boot 2.6.x

  1. revert matching strategy spring.mvc.pathmatch.matching-strategy to ant-path-matcher
  2. hack springfox WebMvcRequestHandlerProvider to filter out actuator controllers which don't respect spring.mvc.pathmatch.matching-strategy
	public WebMvcRequestHandlerProvider(Optional<ServletContext> servletContext, HandlerMethodResolver methodResolver,
			List<RequestMappingInfoHandlerMapping> handlerMappings) {
		this.handlerMappings = handlerMappings.stream().filter(mapping -> mapping.getPatternParser() == null)
				.collect(Collectors.toList());

quaff avatar Nov 25 '21 01:11 quaff

Workaround for Spring Boot 2.6.x

  1. revert matching strategy spring.mvc.pathmatch.matching-strategy to ant-path-matcher
  2. hack springfox WebMvcRequestHandlerProvider to filter out actuator controllers which don't respect spring.mvc.pathmatch.matching-strategy
	public WebMvcRequestHandlerProvider(Optional<ServletContext> servletContext, HandlerMethodResolver methodResolver,
			List<RequestMappingInfoHandlerMapping> handlerMappings) {
		this.handlerMappings = handlerMappings.stream().filter(mapping -> mapping.getPatternParser() == null)
				.collect(Collectors.toList());

how you hack it?

artem-cgi avatar Nov 25 '21 14:11 artem-cgi

Workaround for Spring Boot 2.6.x

  1. revert matching strategy spring.mvc.pathmatch.matching-strategy to ant-path-matcher
  2. hack springfox WebMvcRequestHandlerProvider to filter out actuator controllers which don't respect spring.mvc.pathmatch.matching-strategy
	public WebMvcRequestHandlerProvider(Optional<ServletContext> servletContext, HandlerMethodResolver methodResolver,
			List<RequestMappingInfoHandlerMapping> handlerMappings) {
		this.handlerMappings = handlerMappings.stream().filter(mapping -> mapping.getPatternParser() == null)
				.collect(Collectors.toList());

How exactly is this "hack"? Can you help us with some more complete info?

renanleandrof avatar Nov 25 '21 16:11 renanleandrof

Workaround for Spring Boot 2.6.x

  1. revert matching strategy spring.mvc.pathmatch.matching-strategy to ant-path-matcher
  2. hack springfox WebMvcRequestHandlerProvider to filter out actuator controllers which don't respect spring.mvc.pathmatch.matching-strategy
	public WebMvcRequestHandlerProvider(Optional<ServletContext> servletContext, HandlerMethodResolver methodResolver,
			List<RequestMappingInfoHandlerMapping> handlerMappings) {
		this.handlerMappings = handlerMappings.stream().filter(mapping -> mapping.getPatternParser() == null)
				.collect(Collectors.toList());

How exactly is this "hack"? Can you help us with some more complete info?

Copy WebMvcRequestHandlerProvider.java to your project main source directory, then modify this line, that's why I call it hack not workaround.

quaff avatar Nov 26 '21 00:11 quaff

Thanks for the answer, but this option does not work for me. It comes with more issues such as:

Failed to process import candidates for configuration class [springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'webMvcRequestHandlerProvider' for bean class [springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider] conflicts with existing, non-compatible bean definition of same name and class [test.com.WebMvcRequestHandlerProvider]

alexandreJavaDeveloper avatar Nov 26 '21 09:11 alexandreJavaDeveloper

Thanks for the answer, but this option does not work for me. It comes with more issues such as:

Failed to process import candidates for configuration class [springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'webMvcRequestHandlerProvider' for bean class [springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider] conflicts with existing, non-compatible bean definition of same name and class [test.com.WebMvcRequestHandlerProvider]

You shouldn't change the package name to your own test.com.

quaff avatar Nov 29 '21 00:11 quaff

Thanks @quaff but I put "test.com" just to not add here all package of my private project.

alexandreJavaDeveloper avatar Nov 29 '21 08:11 alexandreJavaDeveloper

springfox.documentation.spring.web.plugins

I mean you should keep the original package name springfox.documentation.spring.web.plugins, not your own.

quaff avatar Nov 29 '21 09:11 quaff

It worked =) Thanks a lot.

Hope SpringFox can release a new version soon to remove the workaround solution.

alexandreJavaDeveloper avatar Nov 29 '21 10:11 alexandreJavaDeveloper

I don't have a lot of time today but I started this PR https://github.com/springfox/springfox/pull/3936 it's definitely not great because I think a lot of dependencies should be upgraded but it does build and all the tests pass.

I am not sure what @dilipkrish wants to do here, if you upgrade spring you will be forced to upgrade gradle/groovy and some other things.

mbazos avatar Nov 29 '21 21:11 mbazos

Hi, will this be patched to SpringFox 2.9.x or would we need to upgrade to SpringFox 3.0.x?

deepak-auto avatar Nov 30 '21 16:11 deepak-auto

Here is another work-around:

  1. revert matching strategy spring.mvc.pathmatch.matching-strategy to ant-path-matcher
  2. add this bean to your app:
@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
    return new BeanPostProcessor() {

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
            }
            return bean;
        }

        private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
            List<T> copy = mappings.stream()
                    .filter(mapping -> mapping.getPatternParser() == null)
                    .collect(Collectors.toList());
            mappings.clear();
            mappings.addAll(copy);
        }

        @SuppressWarnings("unchecked")
        private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
            try {
                Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
                field.setAccessible(true);
                return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                throw new IllegalStateException(e);
            }
        }
    };
}

maxxedev avatar Dec 01 '21 00:12 maxxedev

👍 Better than mine.

quaff avatar Dec 01 '21 00:12 quaff

Here is another work-around:

  1. revert matching strategy spring.mvc.pathmatch.matching-strategy to ant-path-matcher
  2. add this bean to your app:
@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
    return new BeanPostProcessor() {

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
            }
            return bean;
        }

        private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
            List<T> copy = mappings.stream()
                    .filter(mapping -> mapping.getPatternParser() == null)
                    .collect(Collectors.toList());
            mappings.clear();
            mappings.addAll(copy);
        }

        @SuppressWarnings("unchecked")
        private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
            try {
                Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
                field.setAccessible(true);
                return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                throw new IllegalStateException(e);
            }
        }
    };
}

Can you explain how I need to add this bean to my code? Sorry I am new to all of this. I added the bean to my SwaggerConfig class but getting an error "Cannot resolve method 'findField(java.lang.Class<capture<? extends java.lang.Object>>, java.lang.String)'" at this line: Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");

seanmbowen avatar Dec 09 '21 18:12 seanmbowen

Is there any update on this issue? Should we wait for a new version soon?

teopapath avatar Dec 10 '21 07:12 teopapath

Is there any update on this issue? Should we wait for a new version soon?

I suggest you migrate to springdoc.

quaff avatar Dec 10 '21 07:12 quaff

so this project is not maintained anymore?

teopapath avatar Dec 10 '21 07:12 teopapath

Honestly, I am thinking to migrate also, as I am facing several issue on my 2 projects (even though it worked for 1 project), and the project itself its a bit confusing, as basic example we never know which URL we must to use, and when has a migration, they change the ULR.

alexandreJavaDeveloper avatar Dec 10 '21 08:12 alexandreJavaDeveloper