REST WS, OpenAPI, client with Maven (Spring Boot)
1 Reference
Generate Spring Boot REST Client with Swagger > http://www.baeldung.com/spring-boot-rest-client-swagger-codegen
swagger-codegen-maven-plugin > https://github.com/swagger-api/swagger-codegen/blob/master/modules/swagger-codegen-maven-plugin/README.md
2 Introduction
This article will automatically build a full REST WS client from a OpenAPI (former swagger) spec file.
Note: Although this technique is not specific to Spring Boot, the example assumes we're working on an Spring Boot project.
Warning: This sub page uses swagger-codegen-maven-plugin; see the newer openapi-generator-maven-plugin
3 OpenAPI spec file
Place the OpenAPI spec file inside the Maven project folder:
src/main/resources/openapis/
Note: For the example, the spec file name "BOF-20180712.json" will be used.
Remark: It should work in the same way if using a spec file in .yaml format instead of .json.
4 pom.xml
4.1 Let's configure the Maven project POM file for automatically generate Java client classes for the REST WS out of the OpenAPI (former swagger) spec file
At the 'properties' section, declare the swagger-codegen version:
<swagger.codegen.version>2.3.1</swagger.codegen.version>
At the 'build.pluginManagements.plugins' section, add Eclipse lifecycle mapping:
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>io.swagger</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<versionRange>[${swagger.codegen.version},)</versionRange>
<goals>
<goal>generate</goal>
</goals>
</pluginExecutionFilter>
<action>
<execute />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
At the 'build.plugins' section, add the generation for Java sources out of the OpenAPI spec file:
<plugin>
<!-- Generate Java Client for OpenAPI REST WS: BOF -->
<groupId>io.swagger</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>${swagger.codegen.version}</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/openapis/BOF-20180712.json</inputSpec>
<language>java</language>
<library>resttemplate</library>
<modelPackage>${project.groupId}.bof.model</modelPackage>
<apiPackage>${project.groupId}.bof.api</apiPackage>
<invokerPackage>${project.groupId}.bof.invoker</invokerPackage>
<configOptions>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
Try to build the project for checking if any additional dependency is missing (it might depend on the specific OpenAPI spec file being used).
4.2 Adding, if needed, additional dependencies
If project compilation fails, probably swagger-codegen generated Java sources with "import" statements pointing to dependencies not available in the project.
Fortunately, swagger-codegen also generated a pom.xml file with all the dependencies used by the generated sources:
target/generated-sources/swagger/pom.xml
In our example, a dependency for the deserialization of time fields is needed, so let's added to the project pom.xml:
<dependency>
<groupId>com.github.joschi.jackson</groupId>
<artifactId>jackson-datatype-threetenbp</artifactId>
<version>2.6.4</version>
</dependency>
4.3 Add the generated sources folder to the Eclipse build_class_path
At section "build.plugins":
<plugin>
<!-- adding generated source (it adds a new build_class_path to the project) -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources/swagger/src/main/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
Remark: When using Eclipse 4.8, the plugin version inherited from Spring Boot might not be compatible. If that happens, override it in the 'properties' section as follows:
<build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version>
5 Configure Spring Boot to use the generated OpenAPI client
5.1 BofIntegrationConfig.java
Create this file at the same level than the main Spring Boot application one (annotated with @SpringBoot application):
package edu.cou.gpra_doc;
import edu.cou.igpra.bof.api.ProgramasApi;
import edu.cou.igpra.bof.invoker.ApiClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BofIntegrationConfig {
//REST WS Endpoint 'basePath'
private static final String bofWsBasePath = "https://bofws-bofws-test.cloudapps.uoc.es";
@Bean
public ProgramasApi programasApi() {
return new ProgramasApi(apiClient());
}
/**
* The ApiClient class is used for configuring authentication, the base path of
* the API, common headers, and it’s responsible for executing all API
* requests.<br>
*/
@Bean
public ApiClient apiClient() {
ApiClient apiClient = new ApiClient();
// Endpoint basePath
apiClient.setBasePath(bofWsBasePath);
/*- Sample oauth authentication
OAuth petStoreAuth = (OAuth) apiClient.getAuthentication("petstore_auth");
petStoreAuth.setAccessToken("special-key");
*/
return apiClient;
}
}
5.2 @Import(BofIntegrationConfig.class)
Add the annotation to the Spring Boot application class annotated with "@SpringBootApplication":
...
@SpringBootApplication
@Import(BofIntegrationConfig.class)
...
6 Use the just configured remote OpenAPI REST WS
@Service
public class BofHelper implements Bof {
//Inject the generated OpenAPI client
@Autowired
ProgramasApi programasApi;
//Method that invokes a remote operation via the generated client
public Programa getById(Long id, String X_COU_SCOPE, String idioma, String entity)
throws RestClientException {
Programa p = programasApi.getByIdUsingGET(id, X_COU_SCOPE, idioma, entity);
return p;
}
}
7 Troubleshooting
7.1 SSL certificate validation exception when invoking a remote operation
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
} };
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
;
}