Reputation: 451
I have an REST API project build with Spring Boot and i want to document all my Endpoints. I have implemented swagger to do it and was successfully, but recently my project doesn't run anymore, the configuration is the same that when create the project and Swagger was works.
I'm getting this error when try to run the project:
WARN 17868 --- [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
....
ERROR 17868 --- [ restartedMain] o.s.boot.SpringApplication : Application run failed
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
What am i doing wrong?
This is my project configuration:
Main
package com.red.api;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
@PropertySource(value = "classpath:application.properties")
@PropertySource(value = "classpath:propiedades.properties")
public class BackRedApplication {
public static void main(String[] args) {
SpringApplication.run(BackRedApplication.class, args);
}
@Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Pom.xml
...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.0-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.red.api</groupId>
<artifactId>RED</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>RED</name>
<description>RED</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20200518</version>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.8.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>io</artifactId>
<version>7.1.9</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>7.1.9</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>7.1.9</version>
</dependency>
</dependencies>
...
Configuration Class
package com.red.api.configuracion;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class Configuracion {
@Bean
public RestTemplate rest() {
return new RestTemplate();
}
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
@Bean
public Docket redApi() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("com.co.dejsoftware.red.ws"))
.paths(PathSelectors.any()).build();
}
}
Controller
package com.red.api.ws;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.red.api.interfaces.InterfaceSeguridad;
@RestController
@CrossOrigin
@RequestMapping("/seguridad")
public class WsSeguridad {
private Logger logger = LogManager.getLogger(WsSeguridad.class);
@Autowired
private InterfaceSeguridad servicioSeguridad;
@PostMapping(path="/getToken", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getToken(@RequestHeader(name = "user") String usuario,
@RequestHeader(name = "pwd") String contrasena) {
try {
return ResponseEntity.ok(servicioSeguridad.getToken(usuario, contrasena));
} catch (Exception e) {
logger.error(e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
Interface
package com.red.api.interfaces;
import org.springframework.http.ResponseEntity;
public interface InterfaceSeguridad {
public ResponseEntity<Object> getToken(String usuario, String contrasena);
}
Implementation
package com.red.api.implementacion;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import com.red.api.interfaces.InterfaceSeguridad;
@Service
public class ServicioSeguridad implements InterfaceSeguridad {
private Logger logger = LogManager.getLogger(ServicioSeguridad.class);
public ResponseEntity<Object> getToken(String usuario, String contrasena) {
try {
System.out.println("usuario... " + usuario);
System.out.println("contrasena... " + contrasena);
JSONObject item = new JSONObject();
item.put("description", "Success...");
item.put("usuario", usuario);
item.put("contrasena", contrasena);
String jsonResponse = new JSONObject().put("exito", item).toString();
return ResponseEntity.ok(jsonResponse);
}catch (Exception e) {
logger.error(e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
Upvotes: 5
Views: 12249
Reputation: 751
I had a similar problem, I was trying to upgrade to Spring 2.6.6.
I suggest using openapi generator >= 5.4.0 as it removes springfox as default documentation provider
https://github.com/OpenAPITools/openapi-generator/pull/11181 fixes https://github.com/OpenAPITools/openapi-generator/issues/10409
springdoc is the default
more info on spring boot generator options, see https://openapi-generator.tech/docs/generators/spring/ and search for documentationProvider
Upvotes: 0
Reputation: 3899
The problem seems to be that Spring Boot switched from Ant Path Matching to using a PathPatternParser
, see
The Springfox plugin has not been actively maintained for a while and is thus unable to deal with request mappings with the PathPatternParser
. At least in version 3.0.0.
If one insists on continuing to use Springfox with Spring Boot >= 2.6, one can try to force use of Ant Path Matching by setting
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
Forcing Ant Path Matching on the actuators is a separate problem. It works by injecting the WebMvcEndpointHandlerMapping
that was auto-configured before the change by WebMvcEndpointManagementContextConfiguration
:
@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 = shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes,
corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath),
shouldRegisterLinksMapping);
}
private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment,
String basePath) {
return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath)
|| ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}
There may be a cleverer way by excluding the actuators from being analyzed by Springfox in the first place.
You're mileage may vary. Switching to springdoc is probably the more worthwhile approach.
Upvotes: 4
Reputation: 17510
I know this does not solve your problem directly, but consider moving to springdoc. Springfox is so buggy at this point that is a pain to use. I've moved to springdoc
2 years ago because of its Spring WebFlux support and I am very happy about it. Additionally, it also supports Kotlin Coroutines, which I am not sure Springfox does.
If you decide to migrate, springdoc
even has a migration guide.
Upvotes: 8