OpenApi 3.0.2 | Spring Server Generator | Api/Controller interface naming - spring-mvc

I am trying to workout a generated server side Spring MVC code for a OpenApi 3.0.2 specification.
This is how one of the `paths' look like :-
paths:
/api/v1/int/integrations/{some-path-variable}/some-action:
get:
summary: Summary
description: How to change the generated Api/Controller class name?
operationId: methodName
tags:
- inventory
parameters:
- name: Authorization
other details....
The server side code get generated using the Maven plugin which is configured as :-
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>4.1.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/open-api/myapi.yaml</inputSpec>
<generatorName>spring</generatorName>
<library>spring-boot</library>
<output>${project.build.directory}/generated-openapi/spring</output>
<generateApis>true</generateApis>
<addCompileSourceRoot>true</addCompileSourceRoot>
<artifactVersion>${project.version}</artifactVersion>
<groupId>com.company.division</groupId>
<artifactId>myapi-api</artifactId>
<generateApiTests>true</generateApiTests>
<modelPackage>com.company.division.myapi.generated.model</modelPackage>
<apiPackage>com.company.division.myapi.generated.api</apiPackage>
<generateApiDocumentation>true</generateApiDocumentation>
<configOptions>
<dateLibrary>java8</dateLibrary>
<java8>true</java8>
<interfaceOnly>true</interfaceOnly>
<reactive>false</reactive>
<useBeanValidation>true</useBeanValidation>
<performBeanValidation>true</performBeanValidation>
<useOptional>false</useOptional>
<serviceInterface>true</serviceInterface>
<serviceImplementation>false</serviceImplementation>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
As seen in the plugin configuration, I am only interesed in generating the model classes and Spring Controller Interfaces / API interfaces.
Problem
For the mentioned OpenAPI spec, the generated MVC Controller inerfaces is named as ApiApi. I am guessing that is derived from the path's begining part. I could get rid of the /api/v1/int part but that will generate interface named IntegrationsApi but I wan't it to be named say InventoryApi.
What option we have to control the generated controller interface?

'useTags' creating interface and controller classnames
Setting useTags configOption inside plugin configuration fixed my problem:
<configuration>
<configOptions>
<useTags>true</useTags>
<dateLibrary>java8</dateLibrary>
<java8>true</java8>
<interfaceOnly>true</interfaceOnly>
<reactive>false</reactive>
<useBeanValidation>true</useBeanValidation>
<performBeanValidation>true</performBeanValidation>
<useOptional>false</useOptional>
<serviceInterface>true</serviceInterface>
<serviceImplementation>false</serviceImplementation>
</configOptions>
</configuration>

Related

Exclude LocalDate class for checking by SpotBugs plugin

I'm getting SpotBugs warning with code 'EI_EXPOSE_REP2' for class field
private final LocalDate localDate
I'm trying to exclude this check for java.util.LocalDate class for whole app.
I tried to use annotation #SuppressFBWarnings("EI_EXPOSE_REP2") and it works. But for me this is not good idea of adding this annotation into every place where LocalDate is used. Instead of that I would prefer to add this rule to spotbugs-exclude.xml. And I did so but it's not working (still getting SpotBugs warning). My spotbugs-exclude.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<Match>
<Class name="java.time.LocalDate" />
<Bug code="EI_EXPOSE_REP2" />
</Match>
</FindBugsFilter>
pom.xml
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>${spotbugs.mavenplugin.version}</version>
<executions>
<execution>
<id>spotbugs-verify</id>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<excludeFilterFile>spotbugs-exclude.xml</excludeFilterFile>
</configuration>
</plugin>
So my question: how could I exclude SpotBugs checking for specific class in whole app?
Additional question: could we mix up spotbugs-exclude.xml and annotations #SuppressFBWarnings inside the same app? Is SpotBugs able to recognize both configurations?
I have done more investigations and found similar topic here: https://github.com/spotbugs/spotbugs/issues/1601
So looks like for 4.3.0 version SpotBugs plugin has buggy behavior with checking immutable classes and provide warning EI_EXPOSE_REP and EI_EXPOSE_REP2.
So my decision: migrate from 4.3.0 to 4.2.3 - currently all works.

Publish spring-restdocs html documentation with application

I have spring-boot application with spring-restdocs and I want to create endpoint in that application for generated documentation. What is the best approach to expose endpoint with generated html documentation(by asciidoctor)?
I can include index.html to jar-file but don't really know how to create endpoint that will consume that html and expose outside.This html generated after test-stage and before build-jar-stage.
From official documentation:
You could publish the HTML documentation you created to a static website, or package it up and serve it from the application itself.
e.g. I have index.html in 'build/asctiidoctor/html5' folder and want to create controller that will return that index.html.
According to documentation you can configure your build system (Maven, Gradle) to package HTML into spring-boot jar as static content so that it will be served by Spring Boot 'automagically'
In case of Gradle 4.6 and Spring Boot 2.0.0.RELEASE:
bootJar {
dependsOn asciidoctor
from ("${asciidoctor.outputDir}/html5") {
into 'static/docs'
}
}
Then it can be verified locally via 'localhost:<your-port>/<your-context-path/docs/index.html
To access the api guide locally using spring boot using the url http://localhost:8081/docs/api-guide.html , add the following plugins:
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>${asciidoctor-maven-plugin.version}</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>post-integration-test</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>${spring-restdocs.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>${maven-resources-plugin.version}</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>post-integration-test</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.outputDirectory}/static/docs
</outputDirectory>
<resources>
<resource>
<directory>
${project.build.directory}/generated-docs
</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>`
After generating html out of the AsciiDoc just copy the html files into target/generated-docs (see https://spring.io/guides/gs/testing-restdocs/). Spring-Boot then will take and host the documentation within the endpoint <...>/docs/index.html.
You could use maven-resources-plugin for this job.

Generate feature xml with feature dependencies using maven plugin

I am using the maven-feature-plugin
<plugin>
<groupId>org.apache.karaf.tooling</groupId>
<artifactId>features-maven-plugin</artifactId>
<version>2.3.6</version>
<executions>
<execution>
<id>generate</id>
<phase>generate-resources</phase>
<goals>
<goal>generate-features-xml</goal>
</goals>
<configuration>
<bundles>src/main/resources/bundle.properties</bundles>
<kernelVersion>2.3.6</kernelVersion>
<outputFile>target/features.xml</outputFile>
</configuration>
</execution>
</executions>
</plugin>
This works pretty well but one of my generated features depends on the pax-cdi feature is there a way for me to get the plugin to add this for me? For example I have some dependencies defined in the bundle.properties file that cannot be resolved automatically, could I add a feature in this file as well?
You can add the feature as a maven dependency in your pom:
<dependency>
<groupId>org.ops4j.pax.cdi</groupId>
<artifactId>pax-cdi-features</artifactId>
<version>0.8.0</version>
<classifier>features</classifier>
<type>xml</type>
</dependency>
This will result a feature.xml containing the pax-cdi features (providing that karaf-maven-plugin is configured with <aggregateFeatures>true</aggregateFeatures>).
You can also leave it to the container to pull in the pax-cdi feature. Just edit $KARAF_HOME/etc/org.apache.karaf.features.cfg where you can enlist your pax-cdi-features by adding the maven url to the list of featuresRepositories.
featuresRepositories=....
....,\
mvn:org.ops4j.pax.cdi/pax-cdi-features/0.8.0/xml/features
Then add pax-cdi to the list of boot features
featuresBoot=.....,pax-cdi,...
Karaf will start the bundles of pax-cdi when it boots, so that your bundles can find those cdi packages available.
I think that the karaf convention is that the pax-cdi feature should be provided by the container itself, so you don't need to add those bundles to your feature descriptor.
To use pax-cdi feature in karaf enter the following in your Karaf shell
features:addurl mvn:org.ops4j.pax.cdi/pax-cdi-features/0.8.0/xml/features

Getting JNotify into Maven/Archiva

I am currently working on a project that includes using JNotify to monitor when a directory/file has been created, renamed/modified, and deleted. The project is being built in Java 6, not Java 7. JNotify uses JNI to hook into the native OS to monitor the directory/file. My problem is that I need to get JNotify into our repo but I want it to be built so that the java.library.path (DLL) is packaged with the JNI JAR. How would I go about doing that in Maven?
I was able to find the solution I needed using the following maven plugin: http://code.google.com/p/mavennatives/
You must probably upload the jar manually to your archiva instance.
The repository format is fixed, so you will need to perform the rename after retrieving the artifact. That depends how you intend to use it after it is retrieved.
This is a common pattern is something like this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<stripVersion>true</stripVersion>
</configuration>
<executions>
<execution>
<id>copy-jnotify</id>
<configuration>
<includeArtifactIds>JNotify</includeArtifactIds>
<outputDirectory>${project.build.directory}/my-app</outputDirectory>
</configuration>
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
</plugin>
You can use this with the appropriate list of artifacts that will all be copied into the target/my-app directory

Bundling wsdl in jar with CXF wsdl2java

I'm working on an implementation that will use a wsdl that I have gotten from a vendor. Our project is running on Spring and CXF, and I'd like to create a jar that will allow me to access this vendor's wsdl services, but I'm running into classpath issues.
Using CXF's wsdl2java I am able to generate code that acts like this:
WSDL_LOCATION = new URL("file:SomeService.wsdl");
The service requires the wsdl to be in the classpath, but I would like to bundle it in the jar so that it is distributable as a stand-alone jar. Using the wsdl2java tool, I am able to specify the string in the URL instantiation to whatever I would like. However, I have not found a combination of a custom string and wsdl file location inside the jar that works.
The only way I have gotten this to work as I want is to put the wsdl file in the same folder that the SomeService.class is and use the following line:
WSDL_LOCATION = TrackService.class.getResource("TrackService_v4.wsdl");
However, this has the downside of me having to manually edit the java code and compile it myself. This is undesirable because we would eventually like to make this process part of our maven build and have wsdl2java do the generation and compilation by itself automatically.
I am OK with the wsdl being anywhere in the jar, but I don't know what to pass in to wsdl2java to have it reference a file inside the jar.
Does anyone have any suggestions or experience doing this?
You need to specify the classpath wsdl location as follows to generate the stubs that uses ClassLoader to load this wsdl as classpath resource:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>2.4.3</version>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-bindings-soap</artifactId>
<version>2.4.3</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${project.build.directory}/generated-sources/cxf
</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/yourWSDL.wsdl</wsdl>
<extraargs>
<extraarg>**-wsdlLocation**</extraarg>
<extraarg>**classpath:yourWSDL.wsdl**</extraarg>
</extraargs>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
I've run into the same issue - I've got the following workaround but I'm still searching for something cleaner.
Keep your wsdls in src/main/resources/wsdl
Do the following when you create your TrackService:
URL wsdlUrl = TrackService.class.getResource( "/wsdl/TrackService_v4.wsdl" );
TrackService service = new TrackService( wsdlUrl );
The ideal solution would be to pass the location as a <wsdlLocation/> element into the CXF wsdl2java plugin. Then your client code could call the default constructor. However the stub code that is generated does not allow you to specify a wsdl file that is on the classpath.
The CXF Documentation solves it in the same way:
URL wsdl = getClass().getResource("wsdl/greeting.wsdl");
SOAPService service = new SOAPService(wsdl, serviceName);
Another option provided is the JaxWsProxyFactoryBean:
JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean();
proxyFactory.setServiceClass(MyService.class);
proxyFactory.setWsdlLocation("/wsdl/MyService.wsdl");
If you also need to adjust the endpoint URL then you could add:
proxyFactory.setAddress("http://192.168.0.2:6666/");

Resources