openapi generation - initialize objects - java

Iam using openapi-generator-maven-plugin for generating code from my yml files.
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>3.3.4</version>
When generating objects from the yml the generated code always generate objects and initilize them with null.
For example:
public class Foo {
#JsonProperty("bar")
private Bar bar = null;
}
Is there a way that the object is initialized with the object itself like:
public class Foo {
#JsonProperty("bar")
private Bar bar = new Bar();
}

Some snippets and links which can help You.
Plugin config in pom.xml:
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>4.2.2</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/docs/openapi/api.yml</inputSpec>
<generatorName>java</generatorName>
<templateDirectory>docs/openapi/template</templateDirectory>
</configuration>
</execution>
</executions>
</plugin>
Copy default templates e.g. from Java module of OpenAPI generator and put all mustache files inside some directory in Your project - check plugin configuration (in my case it is docs/openapi/template).
Find the file pojo.mustache which is a template to generate a POJO files.
You need to understand some basic Mustache syntax at this point. Find sych a fragment:
[...]
{{^isContainer}}
private {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};
{{/isContainer}}
[...]
Change to whatever You want, e.g.:
[...]
{{^isContainer}}
private {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^isPrimitiveType}} = new {{datatypeWithEnum}}(){{/isPrimitiveType}}{{/defaultValue}};
{{/isContainer}}
[...]
This template snippet will generate new TypeYouWantToUse() parts for every non primitive datatype.
Be aware that this is just a simple example how to proceed further. There are many corner cases e.g. enum handling.
FUrther readings:
OpenApi generator
OpenApi generator templating documentation

Related

Jooq 3.9.3 deletes custom generated java files after generate

This is an issue we face with migration from jooq version 3.4.1 to 3.9.3.
We have a setup in which we extend JavaGenerator and override generatePojo(TableDefinition tableDefinition) to create some custom enum from data in database. This enum is created in a bit hackish way, using PrintWriterand writing the data into FooEnum.java file.
Something like this:
public class FooGenerator extends JavaGenerator {
#Override
protected void generatePojo(TableDefinition table) {
super.generatePojo(table);
// this works in jooq 3.4.1 but not in 3.9.3
generateEnumClasses(table); // loads data and produces FooEnum.java with PrintWriter
}
}
What happens is that the FooEnum.java gets generated and then deleted shortly afterwards. Funny enough, if i create Foo.txt file in the directory where enum should be created, this file survives clean install.
It seems that the enum is deleted after first (of two) generate goals:
jooq-codegen-maven:3.9.3:generate
Any ideas why is the enum getting deleted and how to keep the behavior from version 3.4.1 where it survives ?
This custom generator that we use to extend JavaGenerator is supplied to plugin with:
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen-maven</artifactId>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<id>some id</id>
<configuration>
<generator>
<name>org.jooq.util.FooGenerator</name>
// ...
</generator>
</configuration>
</execution>
</executions>
In case anybody else stumbles upon this, it seems that in newer jooq versions there is some cleanup code in
JavaGenerator {
public final void generate(Database db) {
// .... this deletes 'excess' java files
log.info("Removing excess files");
this.empty(this.getStrategy().getFileRoot(),this.scala?".scala":".java", this.files, this.directoriesNotForRemoval);
this.directoriesNotForRemoval.clear();
this.files.clear();
}
}
which deletes excess .java files.
Edit
Here is a link to github issue regarding this feature from Lukas comment.

Get Maven AbstractMojo #parameters pom tag attributes

I'm new on java.
Given that I have a configuration on my pom.xml
<plugin>
<groupId>org.custom.plugin</groupId>
<artifactId>my-maven/artifactId>
<version>1.0-SNAPSHOT</version>
<configuration>
<providerSelection id="someCustomIdGoesHere">2.0</providerSelection>
</configuration>
How do I call the URL attribute ID with in my class?
/**
* The properties files that will be used when reading properties.
*
* #parameter
* #required
*/
private String url;
// hot to get attribute of ID?
That's not possible. The configuration-blocks only supports elements and a small number of attributes for behavior.
So the best you can do is change the configuration to
<configuration>
<url>some_value</url>
</configuration>
Btw, if you are new to Java, then I'm not sure if writing a custom Maven plugin is the best way to learn. You'll be facing a lot of different aspects of Java and Maven.

Using a compile-time environment variable to configure RestApplicationPath

As stated in the documentation of rest-dispatch, the rest application path must be configured in the GIN module via a constant, here "/api/v1":
public class DispatchModule extends AbstractGinModule {
#Override
protected void configure() {
RestDispatchAsyncModule.Builder dispatchBuilder =
new RestDispatchAsyncModule.Builder();
install(dispatchBuilder.build());
bindConstant().annotatedWith(RestApplicationPath.class).to("/api/v1");
}
}
I would like to make the "/api/v1" constant be resolved at compile time, based on an environment variable set by the build system depending on the target environment (prod, dev, etc...), and on other criteria (the build artifact major version...).
The problem is I do not manage to rely on a compile time variable.
Neither TextResource/CssResource nor GWT's deferred binding won't help here, since GWT.create() cannot be used in GIN module. Another option I considered is using a custom Generator, but this seems to be too complex for this very simple need.
How do you solve this problem ?
If you use Maven as your build system, you could leverage the templating-maven-plugin to generate a Java class that will contain static variables defined in your POM file. That generated class will be used by your GWT code.
For example, you would want to populate a BuildConstants class template
public class BuildConstants {
// will be replaced by Maven
public static final String API_VERSION = "${myapi.version}";
}
and using a Maven property:
<myapi.version>v1</myapi.version>
that will be compiled to
public class BuildConstants {
// will be replaced by Maven
public static final String API_VERSION = "v1";
}
and you could reference those constants from within your DispatchModule:
bindConstant().annotatedWith(RestApplicationPath.class).to("/api/" + BuildConstants.API_VERSION);
Here's a sample config of the templating-maven-plugin that I use in a project:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
<version>1.0-alpha-3</version>
<executions>
<execution>
<id>filter-src</id>
<goals>
<goal>filter-sources</goal>
</goals>
<configuration>
<sourceDirectory>${basedir}/src/main/java-templates</sourceDirectory>
<outputDirectory>${project.build.directory}/generated-sources/java-templates
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
There's no reason you couldn't replace the bindConstant() with a #Provides method (or other bind().toProvider(), which would let you use a TextResource and/or deferred-binding, or whatever.
Asn an example (untested though), the code below uses JSNI to read the value from the host page, which makes it runtime dependent (rather than compile-time):
#Provides #RestApplicationPath native String provideRestApplicationPath() /*-{
return $wnd.restApplicationPath;
}-*/;
Following Thomas Broyer suggestion and Simon-Pierre, you could even bind different root .gwt.xml files depending on your maven profile. Then you choose the appropriate Gin module class where your constants are bound.
That is what we do inside the CarStore companion project of GWTP do do Form factors for example.

Version information for Java library, accessible at runtime

For supporting purposes I need to add a version and build identifier to our Java library. The library itself is a toolkit without user interaction which is used in different environments (stand alone Java applications, web applications, Eclipse applications, Maven dependency, ...).
What I want, is a class with some constants giving me the above described information (such as MyAppVersion.BUILD, ...), so that they can be shown e.g. in dialogs, command line output, etc. After my research, there seem to be the following approaches:
add versioning to file name, such as myLibrary-0.1.2.jar; not feasible in our case, since I have no control over the file name when deployed
add information to the MANIFEST.MF and read it programmatically, like described here. I'm not sure however, how robust this approach is in respect to different class loaders (Eclipse, OSGi, application servers, ...) and if the JAR file gets re-packaged, this information is lost
use a version.properties file holding the version, as described here and use a script during build to update the version.properties file
hard code the version information into the class directly and use a script to update this information
Are there any other approaches? The last option seems most "robust" to me, are there any objections against this variant? Is there a Maven plugin which would support updating this information in a MyAppVersion.java file during build?
I would suggest to use the templating-maven-plugin which is created exactly for such purposes.
You create at best a separate module which contains a template class like this (or within your module):
public final class Version {
private static final String VERSION = "${project.version}";
private static final String GROUPID = "${project.groupId}";
private static final String SVN = "${project.scm.developerConnection}";
private static final String SVN_BRANCH = "${scmBranch}";
private static final String REVISION = "${buildNumber}";
public static String getVersion() {
return VERSION;
}
public static String getGroupId() {
return GROUPID;
}
public static String getSVN() {
return SVN;
}
public static String getRevision() {
return REVISION;
}
public static String getSVNBranch() {
return SVN_BRANCH;
}
}
Which you simply put into src/main/java-templates folder plus an appropriate package name. Furthermore you configure the templating-maven-plugin like the following in your pom file:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
<version>1.0-alpha-3</version>
<executions>
<execution>
<goals>
<goal>filter-sources</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
This will generate a class Version which can be used by others and contains the given version. In the above template class you can use any property which is available in your build (things like JENKINS_ID etc.) or things your might define by yourself.
The result is that this class is compiled and packaged into your jar file.
Apart from that you can combine that with the buildnumber-maven-plugin to create the buildNumber which needs to be added to your pom file like this:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.2</version>
<configuration>
<revisionOnScmFailure>UNKNOWN</revisionOnScmFailure>
<getRevisionOnlyOnce>true</getRevisionOnlyOnce>
</configuration>
<executions>
<execution>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
</plugin>
The last option of hardcoding the version is the most robust which seems to be important to you.
If you build using ant, you can write a class (let's call it VersionGenerator) that will generate a java file with version:
package my.cool.package;
public interface Version {
String VERSION = "1.2.3";
}
Call VersionGenerator from ant
And then compile all your code and roll it into a jar. And your jar will contain a freshly generated and compiled Version.class!
VersionGenerator will have the logic of how to name and increase versions

How to avoid the need to specify the WSDL location in a CXF or JAX-WS generated webservice client?

When I generate a webservice client using wsdl2java from CXF (which generates something similar to wsimport), via maven, my services starts with codes like this:
#WebServiceClient(name = "StatusManagement",
wsdlLocation = "c:/some_absolute_path_to_a_wsdl_file.wsdl",
targetNamespace = "http://tempuri.org/")
public class StatusManagement extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://tempuri.org/", "StatusManagement");
public final static QName WSHttpBindingIStatus = new QName("http://tempuri.org/", "WSHttpBinding_IStatus");
static {
URL url = null;
try {
url = new URL("c:/some_absolute_path_to_a_wsdl_file.wsdl");
} catch (MalformedURLException e) {
System.err.println("Can not initialize the default wsdl from c:/some_absolute_path_to_a_wsdl_file.wsdl");
// e.printStackTrace();
}
WSDL_LOCATION = url;
}
The hardcoded absolute path really sucks. The generated class won't work in any other computer other than mine.
The first idea is to put the WSDL file (plus everything it imports, other WSDLs and XSDs) somewhere in a jar-file and classpath it. But we want to avoid this. Since all that thing was generated by CXF and JAXB based in the WSDLs and XSDs, we see no point in needing to know the WSDL at runtime.
The wsdlLocation attribute is intended to override the WSDL location (at least this is what i readed somewhere), and it default value is "". Since we are using maven, we tried to include <wsdlLocation></wsdlLocation> inside the configuration of CXF to try to force the source generator to leave the wsdlLocation blank. However, this simply makes it ignore the XML tag because it is empty. We did a really ugly shameful hack, using <wsdlLocation>" + "</wsdlLocation>.
This changes other places too:
#WebServiceClient(name = "StatusManagement",
wsdlLocation = "" + "",
targetNamespace = "http://tempuri.org/")
public class StatusManagement extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://tempuri.org/", "StatusManagement");
public final static QName WSHttpBindingIStatus = new QName("http://tempuri.org/", "WSHttpBinding_IStatus");
static {
URL url = null;
try {
url = new URL("" + "");
} catch (MalformedURLException e) {
System.err.println("Can not initialize the default wsdl from " + "");
// e.printStackTrace();
}
WSDL_LOCATION = url;
}
So, my questions are:
Does we really need a WSDL location even if all the classes were generated by CXF and JAXB? If yes, why?
If we do not really need the WSDL location, what is the proper and clean way to make CXF not generate it and avoiding it entirely?
What bad side effects we could get with that hack? We still can't test that to see what happens, so if someone could say in advance, it would be nice.
I finally figured out the right answer to this question today.
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${project.basedir}/src/main/resources/wsdl/FooService.wsdl</wsdl>
<wsdlLocation>classpath:wsdl/FooService.wsdl</wsdlLocation>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
Notice that I have prefixed the value in wsdlLocation with classpath:. This tells the plugin that the wsdl will be on the classpath instead of an absolute path. Then it will generate code similar to this:
#WebServiceClient(name = "FooService",
wsdlLocation = "classpath:wsdl/FooService.wsdl",
targetNamespace = "http://org/example/foo")
public class Foo_Service extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://org/example/foo", "Foo");
public final static QName FooSOAPOverHTTP = new QName("http://org/example/foo", "Foo_SOAPOverHTTP");
static {
URL url = Foo_Service.class.getClassLoader().getResource("wsdl/FooService.wsdl");
if (url == null) {
java.util.logging.Logger.getLogger(Foo_Service.class.getName())
.log(java.util.logging.Level.INFO,
"Can not initialize the default wsdl from {0}", "classpath:wsdl/FooService.wsdl");
}
WSDL_LOCATION = url;
}
Note that this only works with version 2.4.1 or newer of the cxf-codegen-plugin.
We use
wsdlLocation = "WEB-INF/wsdl/WSDL.wsdl"
In other words, use a path relative to the classpath.
I believe the WSDL may be needed at runtime for validation of messages during marshal/unmarshal.
For those using org.jvnet.jax-ws-commons:jaxws-maven-plugin to generate a client from WSDL at build-time:
Place the WSDL somewhere in your src/main/resources
Do not prefix the wsdlLocation with classpath:
Do prefix the wsdlLocation with /
Example:
WSDL is stored in /src/main/resources/foo/bar.wsdl
Configure jaxws-maven-plugin with <wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory> and <wsdlLocation>/foo/bar.wsdl</wsdlLocation>
1) In some cases, yes. If the WSDL contains things like Policies and such that direct the runtime behavior, then the WSDL may be required at runtime. Artifacts are not generated for policy related things and such. Also, in some obscure RPC/Literal cases, not all the namespaces that are needed are output in the generated code (per spec). Thus, the wsdl would be needed for them. Obscure cases though.
2) I thought something like would work. What version of CXF? That sounds like a bug. You can try an empty string in there (just spaces). Not sure if that works or not. That said, in your code, you can use the constructor that takes the WSDL URL and just pass null. The wsdl wouldn't be used.
3) Just the limitations above.
I was able to generate
static {
WSDL_LOCATION = null;
}
by configuring pom file to have a null for wsdlurl:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${basedir}/target/generated/src/main/java</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/service.wsdl</wsdl>
<extraargs>
<extraarg>-client</extraarg>
<extraarg>-wsdlLocation</extraarg>
<wsdlurl />
</extraargs>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
Update for CXF 3.1.7
In my case I put the WSDL files in src/main/resources and added this path to my Srouces in Eclipse (Right Click on Project-> Build Path -> Configure Build Path...-> Source[Tab] -> Add Folder).
Here is how my pom file looks like and as can be seen there is NO wsdlLocation
option needed:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${project.build.directory}/generated/cxf</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>classpath:wsdl/FOO_SERVICE.wsdl</wsdl>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
And here is the generated Service. As can be seen the URL is get from ClassLoader and not from the Absolute File-Path
#WebServiceClient(name = "EventService",
wsdlLocation = "classpath:wsdl/FOO_SERVICE.wsdl",
targetNamespace = "http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/")
public class EventService extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/", "EventService");
public final static QName EventPort = new QName("http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/", "EventPort");
static {
URL url = EventService.class.getClassLoader().getResource("wsdl/FOO_SERVICE.wsdl");
if (url == null) {
java.util.logging.Logger.getLogger(EventService.class.getName())
.log(java.util.logging.Level.INFO,
"Can not initialize the default wsdl from {0}", "classpath:wsdl/FOO_SERVICE.wsdl");
}
WSDL_LOCATION = url;
}
Is it possible that you can avoid using wsdl2java? You can straight away use CXF FrontEnd APIs to invoke your SOAP Webservice. The only catch is that you need to create your SEI and VOs on your client end. Here is a sample code.
package com.aranin.weblog4j.client;
import com.aranin.weblog4j.services.BookShelfService;
import com.aranin.weblog4j.vo.BookVO;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
public class DemoClient {
public static void main(String[] args){
String serviceUrl = "http://localhost:8080/weblog4jdemo/bookshelfservice";
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(BookShelfService.class);
factory.setAddress(serviceUrl);
BookShelfService bookService = (BookShelfService) factory.create();
//insert book
BookVO bookVO = new BookVO();
bookVO.setAuthor("Issac Asimov");
bookVO.setBookName("Foundation and Earth");
String result = bookService.insertBook(bookVO);
System.out.println("result : " + result);
bookVO = new BookVO();
bookVO.setAuthor("Issac Asimov");
bookVO.setBookName("Foundation and Empire");
result = bookService.insertBook(bookVO);
System.out.println("result : " + result);
bookVO = new BookVO();
bookVO.setAuthor("Arthur C Clarke");
bookVO.setBookName("Rama Revealed");
result = bookService.insertBook(bookVO);
System.out.println("result : " + result);
//retrieve book
bookVO = bookService.getBook("Foundation and Earth");
System.out.println("book name : " + bookVO.getBookName());
System.out.println("book author : " + bookVO.getAuthor());
}
}
You can see the full tutorial here http://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/
#Martin Devillers solution works fine. For completeness, providing the steps below:
Put your wsdl to resource directory like : src/main/resource
In pom file, add both wsdlDirectory and wsdlLocation(don't miss / at the beginning of wsdlLocation), like below. While wsdlDirectory is used to generate code and wsdlLocation is used at runtime to create dynamic proxy.
<wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
<wsdlLocation>/mydir/my.wsdl</wsdlLocation>
Then in your java code(with no-arg constructor):
MyPort myPort = new MyPortService().getMyPort();
Here is the full code generation part in pom file, with fluent api in generated code.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>2.5</version>
<dependencies>
<dependency>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-fluent-api</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-tools</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>wsdl-to-java-generator</id>
<goals>
<goal>wsimport</goal>
</goals>
<configuration>
<xjcArgs>
<xjcArg>-Xfluent-api</xjcArg>
</xjcArgs>
<keep>true</keep>
<wsdlDirectory>src/main/resources/package</wsdlDirectory>
<wsdlLocation>/package/my.wsdl</wsdlLocation>
<sourceDestDir>${project.build.directory}/generated-sources/annotations/jaxb</sourceDestDir>
<packageName>full.package.here</packageName>
</configuration>
</execution>
</executions>
Seriously, the top answer is not working for me.
tried cxf.version 2.4.1 and 3.0.10. and generate absolute path with wsdlLocation every times.
My solution is to use the wsdl2java command in the apache-cxf-3.0.10\bin\
with -wsdlLocation classpath:wsdl/QueryService.wsdl.
Detail:
wsdl2java -encoding utf-8 -p com.jeiao.boss.testQueryService -impl -wsdlLocation classpath:wsdl/testQueryService.wsdl http://127.0.0.1:9999/platf/testQueryService?wsdl
I was able to produce autogenerated
static {
WSDL_LOCATION = null;
}
with
<wsdlLocation>null</wsdlLocation>
Though, the purpose of this seems limited for me.. I was trying to achieve as fast as possible short-living WS Client, minimizing initialization on svc = new MyService(); binding = svc.getMySoapPort();, but now I see
INFO: Creating Service {http://www.example.com/MyApi}MyApiService from class com.company.MyApiPortType
instead of
INFO: Creating Service {http://www.example.com/MyApi}MyApiService from WSDL ...
It seems, new MyService(); is costly anyway, so I must think on re-use of long-lived concurrent instance of it... So runtime parsing of WSDL will not be a problem then.

Categories

Resources