Step 1. Add the JitPack repository to your build file
Add it in your root settings.gradle at the end of repositories:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
Add it in your settings.gradle.kts at the end of repositories:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
Add to pom.xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
Add it in your build.sbt at the end of resolvers:
resolvers += "jitpack" at "https://jitpack.io"
Add it in your project.clj at the end of repositories:
:repositories [["jitpack" "https://jitpack.io"]]
Step 2. Add the dependency
dependencies {
implementation 'com.github.sdhays:vertx-codegen:3.3.2'
}
dependencies {
implementation("com.github.sdhays:vertx-codegen:3.3.2")
}
<dependency>
<groupId>com.github.sdhays</groupId>
<artifactId>vertx-codegen</artifactId>
<version>3.3.2</version>
</dependency>
libraryDependencies += "com.github.sdhays" % "vertx-codegen" % "3.3.2"
:dependencies [[com.github.sdhays/vertx-codegen "3.3.2"]]
This projects contains tools which allow idiomatic other language API shims to be generated from Java APIs.
A code generator consist of an MVEL template declared in a codegen.json descriptor:
{
"name": "Groovy",
"generators": [ {
"kind": "class",
"fileName": "'groovy/' + fqn.replace('io.vertx', 'io.vertx.groovy').replace('.', '/') + '.groovy'",
"templateFileName": "vertx-groovy/template/groovy.templ"
} ]
}
fileName is an MVEL expression for the file name, returning null skips the generation.templateFileName is the name of the MVEL template to applyincremental true when the template performs incremental processing, false or absent otherwisekind: there are several kinds of generators for different use cases
class : applied on each API classespackage : applied on each Java packagemodule : applied on each declared module, a module uniquely identifies an APIdataObject: applied on each data object classproxy: applied on each proxy classenum: applied on each enum class annotated with @VertxGenThere can be as many generators as you like.
By default the processor will only validate the source API against the Codegen rules and will not perform code
generation. Code generation will occur when the processor outputDirectory option is configured:
<pluginManagement>
<plugins>
<!-- Configure the execution of the compiler to execute the codegen processor -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
<executions>
<execution>
<id>default-compile</id>
<configuration>
<annotationProcessors>
<annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor>
</annotationProcessors>
<compilerArgs>
<arg>-AoutputDirectory=${project.basedir}/src/main</arg>
</compilerArgs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
In order for code generation to work effectively, certain constraints are put on the Java interfaces.
The constraints are
io.vertx.codegen.annotations.VertxGen annotationio.vertx.codegen.annotations.Fluent annotationio.vertx.codegen.annotations.DataObject annotationio.vertx.core.json.JsonObject parameter.io.vertx.codegen.annotations.CacheReturn annotation@VertxGen, although this is not mandatory to allow the usage of existing Java enumsWe define the following set B of basic types:
java.lang.StringWe define J as the set of types io.vertx.core.json.JsonObject and io.vertx.core.json.JsonArray
We define V as the set of user defined API types which are defined in its own interface and annotated with @VertxGen
The following set P of types are permitted as parameters to any API method:
Bjava.lang.ObjectVJ@DataObjectjava.util.List<C> or java.util.Set<C> where C contains
BVJjava.util.Map<String, C> where C contains
BJVjava.lang.Throwable class, for instance java.lang.Exception is not supportedio.vertx.java.core.Handler<io.vertx.java.core.AsyncResult<HA>> where HA contains
BVJjava.lang.Voidjava.lang.Throwablejava.util.List<C>, java.util.Set<C> or java.util.Map<String, C> where C contains
BVJio.vertx.java.core.Handler<H> where H contains
HAjava.lang.ThrowableThe following set R of types are permitted as return types from any API method:
voidBVJjava.lang.Throwablejava.util.List<C>, java.util.Set<C> or java.util.Map<String, C> where C contains
BJVYou may add static factory methods in your interfaces, e.g.
interface MyInterface {
static MyInterface newInterface(String foo) {
return new ....
}
}
Interfaces can extend other interfaces which also have the @VertxGen annotation.
Interfaces annotated with @VertxGen can either be concrete or abstract, such information is important
for languages not supporting multiple class inheritance like Groovy:
@VertxGen(concrete = false) are meant to be extended by concrete interfaces and
can inherit from abstract interfaces only.@VertxGen or @VertxGen(concrete = true) are implemented directly by Vertx
and can inherit at most one other concrete interface and any abstract interfaceIf you do not wish a method to be used for generation you can annotate it with the @GenIgnore annotation.
Generated types must belong to a module: a java package annotated with @ModuleGen that defines a module. Such
file is created in a file package-info.java.
A module must define:
name used when generating languages that don't follow Java package naming, like JavaScript or Ruby.groupPackage to define the package of the group used for generating the generated package names
(for Groovy, RxJava or Ceylon generation):@ModuleGen(name = "acme", groupPackage="com.acme")
package com.acme.myservice;
The group package must be a prefix of the annotated module, it defines the naming of the generate packages o for the modules that belongs to the same group, in this case:
com.acme.groovy... for Groovy APIcom.acme.rxjava... for RxJava APIFor this particular com.acme.myservice module we have:
com.acme.groovy.myservice for Groovy APIcom.acme.rxjava.myservice for RxJava APIVert.x Apis uses the io.vertx group package and vertx-XYZ name, this naming is exclusively reserved
to Vert.x Apis.
NOTE: using Maven coordinates for name and group package is encouraged: the name corresponding to the
Maven artifactId and the group package corresponding to the groupId.
A data object is a plain Java public class annotated with @DataObject that follows these minimum requirements:
io.vertx.core.json.JsonObject parameter typeOptionally a data object can define a public io.vertx.core.json.JsonObject toJson() method: such method makes the
data object convertible to JsonObject, the data object can then be used as an Api return type.
By default, a data object is responsible to decode from Json (via the JsonObject constructor) and encode
to Json (via the toJson method).
Data object converter can be generated with @DataObject(generateConverter=true) by Vert.x Core. Such
Data object conversion recognize the following types as member of any @DataObject:
io.vertx.core.Buffer typeBJ@DataObjectjava.util.List<C> where C contains
io.vertx.core.Buffer typeBJ@DataObjectList<Object> acts like a JsonArrayjava.util.Map<String, C> where C contains
io.vertx.core.Buffer typeBJ@DataObjectMap<String, Object> acts like a JsonMapThis is also used for data object cheatsheet generation.
Enum types can be freely used in an API, custom enum types should be annotated with @VertxGen
to allow processing of the enum. This is not mandatory to allow the reuse the existing Java enums.
Enums can be processed for providing more idiomatic APIs in some languages.
We use MVEL templating to generate APIs.
There should be a single MVEL template for each language API that is to be generated.
The template will be called once for each interface that is annotated with @VertxGen in the Java API. One
output file (e.g. one .js file) will be created for each Java interface.
The following variables are made available to templates:
ifaceSimpleName - the simple name of the Java interfaceifaceFQCN - the fully qualified class name of the Java interfaceifacePackageName - the name of the Java package the Java interface belongs toifaceComment - the class comment from the Java interfaceconcrete - true when the interface is implemented by vert.x useful to decide the generation of a class or interface in the API shimhelper - a helper class that of type io.vertx.codegen.Helper which contains useful methods for things such as
converting CamelCase to underscores.methods - a list of MethodInfo objects describing each method in the interface.referencedTypes - a list of strings representing the set of user defined types (also annotated with VertxGen) which
are referenced from the current interfacesuperTypes - a list of TypeInfo representing the set of user defined types which the current interface extends fromconcreteSuperType - the concrete super type or nullabstractSuperTypes - subset of superTypes which are abstractmethodMap - this is a Map<String, MethodInfo> - which allows you to look up all methods with a given nameimportedTypes- this is a Set<TypeInfo> containing the types used by this classThe TypeInfo represents a Java type:
name. Generates a string of a form suitable for representing this type in source code using qualified names, for instance io.vertx.core.Handler<io.vertx.core.buffer.Buffer>simpleName. Generates a string of a form suitable for representing this type in source code using simple names, for instance Handler<Buffer>toString. Same as namecollectImports(Collection<TypeInfo.Class> imports). Collect all imports required by this typeThe TypeInfo.Class is a subclass of TypeInfo representing a Java class:
kind. An enum providing more information about the type
STRING, BOXED_PRIMITIVE, PRIMITIVE: basic typesJSON_OBJECT, JSON_ARRAY: io.vertx.core.json.JsonObject and io.vertx.core.json.JsonArrayTHROWABLE: java.lang.ThrowableVOID: java.lang.VoidOBJECT: java.lang.ObjectLIST, SET: corresponding java collectionsAPI: a type annotated with @VertxGenDATA_OBJECT: a type annotations with @DataObjectHANDLER: io.vertx.core.HandlerASYNC_RESULT: io.vertx.core.AsyncResultENUM: An enumOTHER: anything elseThe MethodInfo object has the following fields:
name. The name of the methodkind. The method kind
HANDLER: last parameter type is io.vertx.core.Handler<T> and a void or fluent returnFUTURE: last parameter type is io.vertx.core.Handler<io.vertx.core.AsyncResult<T>> and a void or fluent returnINDEX_GETTER: an index getterINDEX_SETTER: an index setterOTHER: anything elsereturnType. The fully qualified return type (or void) of the methodfluent. true if the method is fluent (i.e. returns a reference to the interface itself for chaining calls)cacheReturn. true if the generated API method should cache return valuecomment. Method comment.params. List of ParamInfo objects representing the parameters of the method.staticMethod. true if it's a static method.typeParams. The list of the type parameters declared by the methodThe ParamInfo object has the following fields:
name. The name of the parametertype. The type of the parameter as a TypeInfodataObject. true If the parameter is a data object type.type - the TypeInfo of the current optiondoc - the Doc objectconcrete" - true when the option is implemented by vert.x - useful to decide the generation of a class or interface in the API shimgenerateConverter - whether a converter should be generated or notinheritConverter - whether the generated converter should convert all propertiesproperties - a Set<PropertyInfo> of the available properties in this optionimportedTypes - the imported types by the optionsuperTypes - a list of TypeInfo representing the set of user defined types which the current interface extends fromsuperType - the supertype of this option ???jsonifiable - True if the object has a toJson() methodThe PropertyInfo object has the following fields:
name. The name of the propertytype. The type of the property as TypeInfodoc - the Doc objectdeclared. True if the property is declared by its data object and does not override the same property from an ancestorwriterMethod. The name of the setter/adder method in JavareaderMethod. The optional name of the getter method in Javaarray. True if the property is an arrayadder. True if the property is an adder (addSomething)jsonifiable. True if the object can be converted somehow to jsonIncremental templating allows the same template to process several models and create a single result. This is
useful when several sources files needs to generate a same file and the output is the result of the models. To
achieve incremental processing, a generator must declares "incremental": true in its descriptor.
During the processing phase, the codegen processors collects all the files generated by incremental templates and groups them by file name. Obviously, the fileName expression of the generator needs to return an appropriate string.
At the end of the processing phase, templates are invoked for each model, pretty much like the normal templating but with the following differences:
incrementalIndex gives the sequence number of the current model, starting at 0incrementalSize gives the total number of models processed by the templatesession is a map provided that allows the template to maintain stateFor instance the template:
@if{incrementalIndex==0}
<html>\n
<body>\n
<ul>\n
@end{}
<li>@{type.name}</li>\n
@if{incrementalIndex==incrementalSize-1}
</ul>\n
</body>\n
</html>\n
@end{}
With codegen.json:
{
"name": "index",
"generators": [ {
"kind": "class",
"incremental": true,
"fileName": "'index.html'",
"templateFileName": "html-index.templ"
} ]
}
Generates an HTML page with the name of all the API classes.