# Code Generation
es4x
works with vert.x code generation. Vert.x code generation is an annotation processing tool that extracts metadata
from the java source code and allows other projects such as es4x
to generate other sources.
A typical code generation process will generate the following files:
package.json
a module descriptor with references to java artifacts and/or other js dependenciesREADME.md
a simple read meindex.js
a commonjs style script exporting all the annotated java interfacesindex.mjs
a ES6 module script exporting all the annotated java interfacesindex.d.ts
a typescript definition file with all the annotated java interfacesoptions.js
a commonjs style script exporting all the annotated java data objectsoptions.mjs
a ES6 module script exporting all the annotated java data objectsoptions.d.ts
a typescript definition file with all the annotated data objectsenums.js
a commonjs style script exporting all the annotated java enumsenums.mjs
a ES6 module script exporting all the annotated java enumsenums.d.ts
a typescript definition file with all the annotated java enumsmod.js
a single ES6 module re-exportingindex.mjs
,options.mjs
andenums.mjs
from a single source
At the moment the current generation of code is driven by a maven
build. The process can be seen as a sub module of
a maven build defined as usual by a pom.xml
file.
# Code generating a vert.x module for es4x
For this example, let's consider the module hot-reload (opens new window). The module is a vertx
codegen module if the APIs follow the codegen restrictions and interfaces are annotated with @VertxGen
like in this
interface HotReload.java (opens new window)
WARNING
Sometimes codegen seem not to be working because a top level package-info.java
file is missing. Don't forget that file
it is required to get the annotation processor to run.
In order to generate an es4x
module for this component all we need is a pom.xml
which should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.reactiverse.es4x</groupId>
<artifactId>es4x-generator</artifactId>
<version>0.16.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>hot-reload</artifactId>
<version>0.15.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.groupId>xyz.jetdrone</maven.groupId>
<npm-version>0.0.5</npm-version>
<npm-skip>false</npm-skip>
<!-- language=json -->
<npm-dependencies>
{
"@vertx/web": "${stack.version}"
}
</npm-dependencies>
</properties>
<dependencies>
<!-- Optional -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>${stack.version}</version>
<optional>true</optional>
</dependency>
<!-- SOURCE TO GENERATE -->
<dependency>
<groupId>${maven.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${npm-version}</version>
</dependency>
<dependency>
<groupId>${maven.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${npm-version}</version>
<scope>provided</scope>
<classifier>sources</classifier>
</dependency>
</dependencies>
</project>
The artifactId
should be the same as the one we want to generate the es4x
module. In this example the original
module has the following coordinates:
<groupId>xyz.jetdrone</groupId>
<artifactId>hot-reload</artifactId>
<version>0.0.5</version>
From here, there are several properties you need to declare, not all of them are required and some already have been prefilled here's the list:
<es4x.version>${project.parent.version}</es4x.version>
<!-- This is the source group id -->
<maven.groupId>io.vertx</maven.groupId>
<npm-skip>true</npm-skip>
<npm-tag>latest</npm-tag>
<maven-deploy-skip>true</maven-deploy-skip>
<maven-gpg-skip>true</maven-gpg-skip>
<!-- language=json -->
<scope-registry>
[
{
"group": "io.vertx",
"prefix": "vertx-",
"scope": "vertx",
"stripPrefix": true
},
{
"group": "io.vertx",
"module": "vertx",
"name": "core",
"scope": "vertx"
},
{
"group": "io.vertx",
"module": "vertx-jdbc",
"name": "jdbc-client",
"scope": "vertx"
},
{
"group": "io.vertx",
"module": "vertx-mongo",
"name": "mongo-client",
"scope": "vertx"
},
{
"group": "io.vertx",
"module": "vertx-redis",
"name": "redis-client",
"scope": "vertx"
},
{
"group": "io.vertx",
"module": "vertx-sql",
"name": "jdbc-client",
"scope": "vertx"
},
{
"group": "io.reactiverse",
"scope": "reactiverse"
}
]
</scope-registry>
<!-- allow to publish under a custom name -->
<npm-name>${project.artifactId}</npm-name>
<npm-version>${project.version}</npm-version>
<npm-license>Apache-2.0</npm-license>
<!-- allow to publish to a different registry -->
<npm-registry>https://registry.npmjs.org</npm-registry>
<!-- allow override of dependencies -->
<!-- language=json -->
<npm-dependencies>
{
"@vertx/core": "${stack.version}"
}
</npm-dependencies>
<!-- language=json -->
<npm-dev-dependencies>{}</npm-dev-dependencies>
<!-- language=json -->
<package-json>
{
"name": "${npm-name}",
"description": "${project.description}",
"version": "${npm-version}",
"license": "${npm-license}",
"public": true,
"maven": {
"groupId": "${maven.groupId}",
"artifactId": "${project.artifactId}",
"version": "${npm-version}"
},
"dependencies": ${npm-dependencies},
"devDependencies": ${npm-dev-dependencies}
}
</package-json>
<!-- language=json -->
<npm-optional-dependencies>[]</npm-optional-dependencies>
<!-- language=json -->
<npm-class-exclusions>[]</npm-class-exclusions>
<!-- language=json -->
<jvm-classes>{}</jvm-classes>
<!-- git -->
<git-url>https://github.com/reactiverse/es4x.git</git-url>
<git-directory>/generator/${maven.groupId}/${project.artifactId}</git-directory>
Finally you need to go over the dependencies section. There are 2 dependencies you always need to have:
<dependency>
<groupId>${maven.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${npm-version}</version>
</dependency>
<dependency>
<groupId>${maven.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${npm-version}</version>
<scope>provided</scope>
<classifier>sources</classifier>
</dependency>
The final artifact and the artifact source jar. Using the artifact we can compute the dependencies so we can ensure that the annotation processor can work outside the source project, but there are cases like in this example where optional dependencies are listed. If such dependencies are used, they need to be re-declared in the pom like in the example above.
# Running the generation
Running the generation is a typical maven phase (generate-sources
) so all you need is:
~/ $ mvn clean generate-sources
[INFO] Scanning for projects...
[INFO]
[INFO] -------------------< io.reactiverse.es4x:hot-reload >-------------------
[INFO] Building hot-reload 0.15.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hot-reload ---
[INFO] Deleting /home/paulo/Projects/reactiverse/es4x/generator/xyz.jetdrone/hot-reload/target
[INFO]
[INFO] --- maven-enforcer-plugin:1.4.1:enforce (enforce-maven) @ hot-reload ---
[INFO]
[INFO] --- properties-maven-plugin:1.0.0:set-system-properties (default) @ hot-reload ---
[INFO] Set 8 system properties
[INFO]
[INFO] --- maven-dependency-plugin:3.2.0:unpack-dependencies (unpack-sources) @ hot-reload ---
[INFO] Unpacking /home/paulo/.m2/repository/xyz/jetdrone/hot-reload/0.0.6/hot-reload-0.0.6-sources.jar to /home/paulo/Projects/reactiverse/es4x/generator/xyz.jetdrone/hot-reload/target/sources/hot-reload with includes "**/*.java" and excludes "**/examples/**/*.*,**/*-examples/**/*.*,**/groovy/**/*.*,**/rxjava/**/*.*,**/reactivex/**/*.*"
[INFO]
[INFO] --- maven-processor-plugin:4.5-jdk8:process (generate-api) @ hot-reload ---
[WARNING] diagnostic: warning: [options] bootstrap class path not set in conjunction with -source 8
[INFO] diagnostic: Note: Loaded es4x-generator (index.js) code generator
[INFO] diagnostic: Note: Loaded es4x-generator (options.js) code generator
[INFO] diagnostic: Note: Loaded es4x-generator (enum.js) code generator
[INFO] diagnostic: Note: Loaded es4x-generator (index.d.ts) code generator
[INFO] diagnostic: Note: Loaded es4x-generator (options.d.ts) code generator
[INFO] diagnostic: Note: Loaded es4x-generator (enum.d.ts) code generator
[INFO] diagnostic: Note: Loaded es4x-generator (index.mjs) code generator
[INFO] diagnostic: Note: Loaded es4x-generator (options.mjs) code generator
[INFO] diagnostic: Note: Loaded es4x-generator (enum.mjs) code generator
[INFO] diagnostic: Note: Loaded es4x-generator (package.json) code generator
[INFO] diagnostic: Note: Loaded es4x-generator (README.md) code generator
[INFO] diagnostic: Note: Loaded es4x-generator (mod.js) code generator
[INFO] diagnostic: Note: Loaded data_object_converters code generator
[INFO] diagnostic: Note: Generated model xyz.jetdrone.vertx.hot.reload.HotReload: npm/index.d.ts
[INFO] diagnostic: Note: Generated model xyz.jetdrone.vertx.hot.reload.HotReload: npm/package.json
[INFO] diagnostic: Note: Generated model xyz.jetdrone.vertx.hot.reload.HotReload: npm/index.js
[INFO] diagnostic: Note: Generated model xyz.jetdrone.vertx.hot.reload.HotReload: npm/mod.js
[INFO] diagnostic: Note: Generated model xyz.jetdrone.vertx.hot.reload.HotReload: npm/index.mjs
[INFO] diagnostic: Note: Generated model xyz.jetdrone.vertx.hot.reload.HotReload: npm/README.md
[WARNING] diagnostic: warning: Unclosed files for the types 'PathForCodeGenProcessor'; these types will not undergo annotation processing
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:copy-resources (copy-extras) @ hot-reload ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] skip non existing resourceDirectory /home/paulo/Projects/reactiverse/es4x/generator/xyz.jetdrone/hot-reload/extra
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.371 s
[INFO] Finished at: 2021-09-10T21:32:03+02:00
[INFO] ------------------------------------------------------------------------
~/Projects/reactiverse/es4x/generator/xyz.jetdrone/hot-reload (develop)$
# Working around limitations
Codegen is a great tool but sometimes is is very hard to get it to work for all cases. For this es4x
codegen plugin
has several workarounds to allow you to quickly hack your desired output without having to write code.
# Adding code to a generated file
You can either add code to the beginning or to the end of each generated file. At the same level of your pom.xml
add
a file {index|options|enums|...}.header.{js|mjs|.d.ts}
and it's content will be prepended to the generated file.
The same can be done to the end of the generated files, use the following format: {index|options|enums|...}.footer.{js|mjs|.d.ts}
.
For example, vert.x core doesn't annotate the types JsonObject
and JsonArray
so in order to expose them in core we
have a file module.header.mjs
with the following content:
export const AsyncResult = Java.type('io.vertx.core.AsyncResult');
export const JsonObject = Java.type('io.vertx.core.json.JsonObject');
export const JsonArray = Java.type('io.vertx.core.json.JsonArray');
The same can be done to a specific type. For example, HttpMethod
isn't fully annotated so we include the default
methods to right interface during the index.d.ts
file generation by creating an includes file HttpMethod.includes.json
.
{
"d.ts": " /**\n * The RFC 2616 {@code OPTIONS} method, this instance is interned and uniquely us..."
}
The key identifies the kind of generation template where the inclusion should happen.
WARNING
Exclusions can also be done using .excludes.json
file name suffix.
# Overriding
Just like inclusion and exclusion, we can also override method parameters or even return types using the file name
suffix .override.json
, for example, CompositeFuture
codegen doesn't handle some overloads correctly so we can
define an overrides file as:
{
"cause": "index?: number",
"succeeded": "index?: number",
"failed": "index?: number",
"isComplete": "index?: number",
"setHandler": {
"return": "Future<CompositeFuture>"
}
}
This allows as to define an optional argument index
to several methods and override the return type for setHandler
to comply to typescript restrictions.
← Logging