jvacaq
jvacaq

Reputation: 1539

Spring Boot 2.6.0 / Spring fox 3 - Failed to start bean 'documentationPluginsBootstrapper'

I'm trying to initiate a Spring Boot project using OpenJDK 15, Spring Boot 2.6.0, Springfox 3.

We are working on a project that replaced Netty as the web server and used Jetty instead, because we do not need a non-blocking environment.

In the code we depend primarily on Reactor API (Flux, Mono), so we can not remove org.springframework.boot:spring-boot-starter-webflux dependencies.

I replicated the problem that we have in a new project: https://github.com/jvacaq/spring-fox.

I figured out that these lines in our build.gradle file are the origin of the problem.

compile("org.springframework.boot:spring-boot-starter-web") {
   exclude module: "spring-boot-starter-tomcat"
}
compile("org.springframework.boot:spring-boot-starter-jetty")

Here is the build.gradle file:

plugins {
    id 'org.springframework.boot' version '2.6.0'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
    mavenCentral()
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web") {
        exclude module: "spring-boot-starter-tomcat"
    }
    compile("org.springframework.boot:spring-boot-starter-jetty")
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'io.projectreactor:reactor-test'
    implementation "io.springfox:springfox-boot-starter:3.0.0"
}

test {
    useJUnitPlatform()
}

I issued the command gradle clean bootrun. The result is this error:

gradle clean bootrun

> Task :bootRun FAILED

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.6.0)

2021-11-19 09:41:06.665  INFO 16666 --- [           main] c.e.springfox.SpringFoxApplication       : Starting SpringFoxApplication using Java 15.0.2 on advance-Inspiron-5379 with PID 16666 (/home/advance/projects/spring-fox/build/classes/java/main started by advance in /home/advance/projects/spring-fox)
2021-11-19 09:41:06.666  INFO 16666 --- [           main] c.e.springfox.SpringFoxApplication       : No active profile set, falling back to default profiles: default
2021-11-19 09:41:07.294  INFO 16666 --- [           main] org.eclipse.jetty.util.log               : Logging initialized @1132ms to org.eclipse.jetty.util.log.Slf4jLog
2021-11-19 09:41:07.396  INFO 16666 --- [           main] o.s.b.w.e.j.JettyServletWebServerFactory : Server initialized with port: 8080
2021-11-19 09:41:07.398  INFO 16666 --- [           main] org.eclipse.jetty.server.Server          : jetty-9.4.44.v20210927; built: 2021-09-27T23:02:44.612Z; git: 8da83308eeca865e495e53ef315a249d63ba9332; jvm 15.0.2+7-27
2021-11-19 09:41:07.417  INFO 16666 --- [           main] o.e.j.s.h.ContextHandler.application     : Initializing Spring embedded WebApplicationContext
2021-11-19 09:41:07.417  INFO 16666 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 713 ms
2021-11-19 09:41:07.474  INFO 16666 --- [           main] org.eclipse.jetty.server.session         : DefaultSessionIdManager workerName=node0
2021-11-19 09:41:07.474  INFO 16666 --- [           main] org.eclipse.jetty.server.session         : No SessionScavenger set, using defaults
2021-11-19 09:41:07.475  INFO 16666 --- [           main] org.eclipse.jetty.server.session         : node0 Scavenging every 660000ms
2021-11-19 09:41:07.480  INFO 16666 --- [           main] o.e.jetty.server.handler.ContextHandler  : Started o.s.b.w.e.j.JettyEmbeddedWebAppContext@6aa3bfc{application,/,[file:///tmp/jetty-docbase.8080.2024342829220941812/, jar:file:/home/advance/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-swagger-ui/3.0.0/1e665fbe22148f7c36fa8a08e515a0047cd4390b/springfox-swagger-ui-3.0.0.jar!/META-INF/resources],AVAILABLE}
2021-11-19 09:41:07.480  INFO 16666 --- [           main] org.eclipse.jetty.server.Server          : Started @1318ms
2021-11-19 09:41:07.920  INFO 16666 --- [           main] o.e.j.s.h.ContextHandler.application     : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-11-19 09:41:07.920  INFO 16666 --- [           main] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2021-11-19 09:41:07.921  INFO 16666 --- [           main] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
2021-11-19 09:41:07.931  INFO 16666 --- [           main] o.e.jetty.server.AbstractConnector       : Started ServerConnector@2643d762{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
2021-11-19 09:41:07.932  INFO 16666 --- [           main] o.s.b.web.embedded.jetty.JettyWebServer  : Jetty started on port(s) 8080 (http/1.1) with context path '/'
2021-11-19 09:41:07.934  WARN 16666 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: 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
2021-11-19 09:41:07.949  INFO 16666 --- [           main] o.e.jetty.server.AbstractConnector       : Stopped ServerConnector@2643d762{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
2021-11-19 09:41:07.950  INFO 16666 --- [           main] org.eclipse.jetty.server.session         : node0 Stopped scavenging
2021-11-19 09:41:07.951  INFO 16666 --- [           main] o.e.j.s.h.ContextHandler.application     : Destroying Spring FrameworkServlet 'dispatcherServlet'
2021-11-19 09:41:07.951  INFO 16666 --- [           main] o.e.jetty.server.handler.ContextHandler  : Stopped o.s.b.w.e.j.JettyEmbeddedWebAppContext@6aa3bfc{application,/,[file:///tmp/jetty-docbase.8080.2024342829220941812/, jar:file:/home/advance/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-swagger-ui/3.0.0/1e665fbe22148f7c36fa8a08e515a0047cd4390b/springfox-swagger-ui-3.0.0.jar!/META-INF/resources],STOPPED}
2021-11-19 09:41:07.958  INFO 16666 --- [           main] ConditionEvaluationReportLoggingListener :

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-11-19 09:41:07.970 ERROR 16666 --- [           main] o.s.boot.SpringApplication               : Application run failed

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:181) ~[spring-context-5.3.13.jar:5.3.13]
        at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.13.jar:5.3.13]
        at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.13.jar:5.3.13]
        at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
        at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-5.3.13.jar:5.3.13]
        at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-5.3.13.jar:5.3.13]
        at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935) ~[spring-context-5.3.13.jar:5.3.13]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.13.jar:5.3.13]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.6.0.jar:2.6.0]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[spring-boot-2.6.0.jar:2.6.0]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412) ~[spring-boot-2.6.0.jar:2.6.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:302) ~[spring-boot-2.6.0.jar:2.6.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.0.jar:2.6.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290) ~[spring-boot-2.6.0.jar:2.6.0]
        at com.example.springfox.SpringFoxApplication.main(SpringFoxApplication.java:10) ~[main/:na]
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.countRunAndMakeAscending(TimSort.java:355) ~[na:na]
        at java.base/java.util.TimSort.sort(TimSort.java:220) ~[na:na]
        at java.base/java.util.Arrays.sort(Arrays.java:1306) ~[na:na]
        at java.base/java.util.ArrayList.sort(ArrayList.java:1721) ~[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:1625) ~[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:178) ~[spring-context-5.3.13.jar:5.3.13]
        ... 14 common frames omitted


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':bootRun'.
> Process 'command '/home/advance/.sdkman/candidates/java/15.0.2-open/bin/java'' finished with non-zero exit value 1

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.9.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD FAILED in 2s
5 actionable tasks: 5 executed

How can I solve it?

Upvotes: 112

Views: 164592

Answers (15)

enesgonez
enesgonez

Reputation: 226

First i added InternalResourceViewResolver and WebMvcEndpointHandlerMapping beans. I can run application without exception. But,when i try to expose actuator on different with management.server.port config, i faced below exception.

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


Description:

Parameter 4 of method webEndpointServletHandlerMapping in com.XXX.SwaggerBaseConfiguration required a bean of type 'org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties' that could not be found.

Action:

Consider defining a bean of type 'org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties' in your configuration.

Finally, i removed my custom WebMvcEndpointHandlerMappingand bean and my config class is like below. With this, i can run actutor on same port and different port,too.

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Configuration
public class SwaggerBaseConfiguration {

    @Bean
    public InternalResourceViewResolver defaultViewResolver() {
        return new InternalResourceViewResolver();
    }

    @Bean
    public 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");
                    if (Objects.isNull(field)) {
                        return new ArrayList<>();
                    }
                    field.setAccessible(true);
                    return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    throw new IllegalStateException(e);
                }
            }
        };
    }

}

Upvotes: 0

Just create or modify configuration file with adding @EnableWebMvc annotation, sth like this:

@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurationSupport {}

Upvotes: 1

Jay Yadav
Jay Yadav

Reputation: 440

These steps worked for me with SpringBoot version > 2.6
One thing to note it that dependency io.springfox:springfox-boot-starter:3.0.0 doesnt works well with SpringBoot v > 2.6
See this for more info: https://stackoverflow.com/a/64333853
If your springboot version is less than 2.6 follow this answer :
https://stackoverflow.com/a/74433644/16769477

Step 1: Added following dependencies in build.gradle for springfox.

implementation "io.springfox:springfox-swagger-ui:2.9.2"
implementation "io.springfox:springfox-swagger2:2.9.2"
implementation "io.springfox:springfox-schema:2.9.2"

Step 2: Added this property in application.properties

spring.mvc.pathmatch.matching-strategy = "ANT_PATH_MATCHER"

Step 3: Created a config file as

@EnableSwagger2
@Configuration
public class SpringFoxSwaggerConfig {

    @Bean
    public Docket apiDocket() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()
                .apiInfo(metadata());
    }

    private ApiInfo metadata() {
        return new ApiInfoBuilder()
                .title("")
                .description("")
                .version("")
                .version("2.0")
                .contact(new Contact(null, null, "this.developer"))
                .build();
    }
}

clean and build the project
Swagger endpoint should be accessible at these endpoints

http://host-name/servlet-context/v2/api-docs
http://host-name/servlet-context/swagger-ui/index.html

Upvotes: 3

Eren
Eren

Reputation: 642

Not a solution but...

I downgraded spring-boot-starter-parent version from 2.6.0 to 2.5.6 and it started to work.

Upvotes: 20

icyfire
icyfire

Reputation: 1270

The workaround mentioned in the below issue comment seems to be working for people with spring actuator as well. Just changing the path matcher won't work for projects using actuator.

  1. Set in application.properties file:

    spring.mvc.pathmatch.matching-strategy to ant_path_matcher

  2. Add the @Bean below:


@Bean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(
                WebEndpointsSupplier webEndpointsSupplier, 
                ServletEndpointsSupplier servletEndpointsSupplier, 
                ControllerEndpointsSupplier controllerEndpointsSupplier, 
                EndpointMediaTypes endpointMediaTypes,
                CorsEndpointProperties corsProperties, 
                WebEndpointProperties webEndpointProperties, 
                Environment environment) {
    List<ExposableEndpoint<?>> allEndpoints = new ArrayList();
    Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
    allEndpoints.addAll(webEndpoints);
    allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
    allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
    String basePath = webEndpointProperties.getBasePath();
    EndpointMapping endpointMapping = new EndpointMapping(basePath);
    boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(
                webEndpointProperties, environment, basePath);
    return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, 
                endpointMediaTypes, corsProperties.toCorsConfiguration(), 
                new EndpointLinksResolver(allEndpoints, basePath), 
                shouldRegisterLinksMapping, null);
}

private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, 
                                           Environment environment, String basePath) {
    return webEndpointProperties.getDiscovery().isEnabled() && 
                (StringUtils.hasText(basePath) || 
                ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}

Reference:

Upvotes: 70

ℛɑƒ&#230;Ŀᴿᴹᴿ
ℛɑƒ&#230;Ŀᴿᴹᴿ

Reputation: 5326

Only adding @EnableWebMvc in the main class should resolve the problem:

@EnableWebMvc
@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class);
    }
}

Upvotes: 39

Mauricio Toledo
Mauricio Toledo

Reputation: 699

I will complete what Anatoly mentioned about using springdoc-openapi.

First add the springdoc-openapi dependency to your Gradle build.gradle (or Maven pom.xml):

// build.gradle
implementation "org.springdoc:springdoc-openapi-ui:1.6.4"

Add OpenAPIDefinition annotation to your main class:

@SpringBootApplication
@OpenAPIDefinition
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

Now you can go to {URI}/swagger-ui/index.html and it will work showing the swagger view.

Upvotes: 9

Atul
Atul

Reputation: 3357

I have similar issue where I am getting the error like this:

springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper: Unable to scan documentation context default

With NullPointerException

This issue I solved by modifying couple of changes.

  1. Instead of this

.apis(RequestHandlerSelectors.basePackage(basePackage))

 I used this- 

.apis(RequestHandlerSelectors.any())

  1. Another change I made is: Add url pattern to get the specific urls only(This is subjective by the way)

.paths(PathSelectors.ant("/external/**"))

The tech stack is:

  • Spring-Boot:2.5.0
  • Swagger:3.0.0

Upvotes: 0

Manas Ranjan Mahapatra
Manas Ranjan Mahapatra

Reputation: 423

Path matching policy switch back to ant_path_matcher Add the below line to the application.properties file

spring.mvc.pathmatch.matching-strategy=ant_path_matcher

Add the following definition of the bean

public class SpringBootApplication {

public static void main(String[] args) {
    SpringApplication.run(SpringBootApplication.class, args);
}
@Bean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier,
        ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier,
        EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties,
        WebEndpointProperties webEndpointProperties, Environment environment) {
    List<ExposableEndpoint<?>> allEndpoints = new ArrayList();
    Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
    allEndpoints.addAll(webEndpoints);
    allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
    allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
    String basePath = webEndpointProperties.getBasePath();
    EndpointMapping endpointMapping = new EndpointMapping(basePath);
    boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment,
            basePath);
    return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes,
            corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath),
            shouldRegisterLinksMapping, null);
}

private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment,
        String basePath) {
    return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath)
        || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}
}

I used the first method and solved the problem after adding just one configuration. If you’ve encountered this problem recently, I hope this article helps you.

Upvotes: 2

David Charo Makuba
David Charo Makuba

Reputation: 51

Just add this to your application.properties file:spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER.

Upvotes: 4

Matt Stephenson
Matt Stephenson

Reputation: 8620

The popular workaround overriding a definition of WebMvcEndpointHandlerMapping was not working for me in some environments with JDK 17, Boot+Actuator 2.6, Springfox 3.

This, however, is working for me everywhere I need it (local development and CloudFoundry containers):

@Bean
public WebMvcRequestHandlerProvider webMvcRequestHandlerProvider(
        Optional<ServletContext> context, 
        HandlerMethodResolver methodResolver, 
        List<RequestMappingInfoHandlerMapping> handlerMappings) {
    handlerMappings = handlerMappings.stream()
         .filter(rh -> rh.getClass().getName().contains("RequestMapping")).toList();
    return new WebMvcRequestHandlerProvider(context, methodResolver, handlerMappings);
}

I wrote a little more about it on the GitHub thread.

Upvotes: 2

Sabir Khan
Sabir Khan

Reputation: 10132

I already had a working system on Spring Boot 1.5.5.RELEASE and things stopped working when I upgraded to Spring Boot version 2.7.2 and 3.0.0 of SpringFox. Followed all steps of Migration Steps but that doesn't solved issue.

Had to do two additional things,

  1. In swagger config class - new Docket(DocumentationType.OAS_30) instead of new Docket(DocumentationType.SWAGGER_2)

  2. @EnableOpenApi on swagger config class

Swagger is finally coming at a new url than previous versions ( /swagger-ui.html )

/swagger-ui/index.html

Other than the mentioned answer took help from

Also, its a REST API project for me so I don't use EnableWebMvc

Upvotes: 0

Wrapper
Wrapper

Reputation: 932

Just if someone is having difficulties with this setup, here is my config which is setup for actuator and spring boot 2.6.6:

In my application.yaml:

swagger:
    title: ^project.name^
    description: ^project.description^
    version: ^project.version^
    termsOfServiceUrl: \#
    contactName: Your name
    contactUrl: \#
    contactEmail: email
    license: License of API
    licenseUrl: \#

Make sure that in your pom.xml within <properties> you place:

<properties>
    <resource.delimiter>^</resource.delimiter>
</properties>

The above is important for following config:

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.web.*;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Getter
@Setter
@Configuration
@EnableSwagger2
@ConfigurationProperties(prefix = "swagger")
public class SwaggerFoxConfig {

    private String title;
    private String description;
    private String version;
    private String termsOfServiceUrl;
    private String license;
    private String licenseUrl;
    private String contactName;
    private String contactUrl;
    private String contactEmail;

    @Bean
    public Docket apiDocket() {
        return new Docket(DocumentationType.SWAGGER_2)
                .useDefaultResponseMessages(false)
                .select()
                .apis(RequestHandlerSelectors.basePackage("path.to.base.package"))
                .paths(PathSelectors.any())
                .build()
                .apiInfo(apiInfo());
    }

    private ApiInfo apiInfo() {
        System.out.println(this.title);
        return new ApiInfo(
                this.title,
                this.description,
                this.version,
                this.termsOfServiceUrl,
                new Contact(this.contactName, this.contactUrl, this.contactEmail),
                this.license,
                this.licenseUrl,
                Collections.emptyList());
    }

    @Bean
    public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) {
        List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
        Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
        allEndpoints.addAll(webEndpoints);
        allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
        allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
        String basePath = webEndpointProperties.getBasePath();
        EndpointMapping endpointMapping = new EndpointMapping(basePath);
        boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
        return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
    }


    private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
        return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
    }
}

Also in your .yaml or .properties have this setup:

spring:
   mvc:
     pathmatch:
         matching-strategy: ANT_PATH_MATCHER
   jpa:
     bla..
    
    
springfox:
  documentation:
    swagger-ui:
      baseUrl: scheduler-service

Back to pom.xml, have your Spring Boot version and Actuator setup:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.6</version> <!-- lookup parent from repository -->
    <relativePath/>
</parent>

...

<properties>
    <spring.fox.version>3.0.4</spring.fox.version>
</properties>
   
<dependencies>
   <dependency>
        <groupId>org.thingsboard</groupId>
        <artifactId>springfox-boot-starter</artifactId>
        <version>${spring.fox.version}</version>
    </dependency>
</dependencies>

You can find Swagger UI at:

http://localhost:8080/your-service-name-from-tag-'name'-in-pom-xml/swagger-ui/index.html

Upvotes: 0

TugbaYavuz
TugbaYavuz

Reputation: 63

Remove anotations

@EnableSwagger2
@SpringBootApplication
@EnableSwagger2
public class Application {}

Upvotes: -3

Andy Wilkinson
Andy Wilkinson

Reputation: 116091

This problem's caused by a bug in Springfox. It's making an assumption about how Spring MVC is set up that doesn't always hold true. Specifically, it's assuming that MVC's path matching will use the Ant-based path matcher and not the PathPattern-based matcher. PathPattern-based matching has been an option for some time now and is the default as of Spring Boot 2.6.

As described in Spring Boot 2.6's release notes, you can restore the configuration that Springfox assumes will be used by setting spring.mvc.pathmatch.matching-strategy to ant-path-matcher in your application.properties file. Note that this will only work if you are not using Spring Boot's Actuator. The Actuator always uses PathPattern-based parsing, irrespective of the configured matching-strategy. A change to Springfox will be required if you want to use it with the Actuator in Spring Boot 2.6 and later.

Upvotes: 140

Related Questions