A service is just a functionality that simplify Vertx code by generating boilerplate code that will handle all event bus operations.
When creating a service there’s a certain amount of code to listen on the event bus for incoming messages, route them to the appropriate method and return results on the event bus. With Vert.x service proxies, that boilerplate code is generated and you can concentrate on writing the service.
To create a Vert.x service, you
Code generation happens through two annotation:
Example:
@ProxyGen // Generate service proxies
@VertxGen // Generate the clients
public interface SomeDatabaseService {
// ...
}
Service annotated with @ProxyGen annotation trigger the generation of the two service helper classes:
The service proxy mechanism relies on code generation, so modifications to the service interface require to re-compile the sources to regenerate the code.
Example:
With the @VertxGen annotation, you generate service stubs in any of the languages supported by Vert.x
For instance, you can write your service once in Java and interact with it with another language.
For this don’t forget to add the dependency on your language in your build descriptor.
How to configure the build tool to configure the annotation processing.
The process code can be found at this addresse CodeGenProcessor.java
See https://vertx.io/docs/guide-for-java-devs/#_maven_configuration_changes
ext {
vertxVersion = '3.8.4'
}
dependencies {
compile "io.vertx:vertx-core:$vertxVersion"
compile "io.vertx:vertx-service-proxy:$vertxVersion:processor"
compile "io.vertx:vertx-codegen:$vertxVersion:processor"
}
dependencies {
implementation "io.vertx:vertx-core:$vertxVersion"
implementation "io.vertx:vertx-service-proxy:$vertxVersion"
compileOnly "io.vertx:vertx-codegen:$vertxVersion"
annotationProcessor "io.vertx:vertx-codegen:$vertxVersion:processor"
// <3.8.4
// annotationProcessor "io.vertx:vertx-service-proxy:$vertxVersion:processor"
// >=3.8.4
annotationProcessor "io.vertx:vertx-service-proxy:$vertxVersion"
}
task annotationProcessing(type: JavaCompile, group: 'build') { // codegen
description 'Generates the stubs'
options.incremental = false
source = sourceSets.main.java
classpath = configurations.compileClasspath
destinationDir = project.file('src/main/generated')
options.annotationProcessorPath = configurations.annotationProcessor
options.debugOptions.debugLevel = "source,lines,vars"
options.compilerArgs = [
"-proc:only",
"-processor", "io.vertx.codegen.CodeGenProcessor",
"-Acodegen.output=${project.projectDir}/src/main"
]
}
compileJava {
dependsOn (annotationProcessing)
}
sourceSets {
main {
java {
srcDirs += 'src/main/generated'
}
}
}
If it's not working, there is also a Plugin
The processor is configured by a few options:
Compiler:
@ProxyGen
public interface SomeDatabaseService {
// A couple of static factory methods to create the instances (respectively implementation and proxy
// Create is used when you deploy a vertical (ie you deploy the service)
@GenIgnore
static SomeDatabaseService create(arguments ..., Handler<AsyncResult<DatabaseServiceInterface>> readyHandler) {
// The SomeDatabaseServiceImpl class is an implementation class that you are creating
return new SomeDatabaseServiceImpl(argument, readyHandler);
}
@GenIgnore
// Proxy is used when you use/test the service as server client code
static SomeDatabaseService createProxy(Vertx vertx, String address) {
// The SomeDatabaseServiceVertxEBProxy class is generated by the annotation processor
return new SomeDatabaseServiceVertxEBProxy(vertx, address);
}
// Actual service operations here...
@Fluent
SomeDatabaseService doSomething(Arguments ..., Handler<AsyncResult<YourReturnType>> resultHandler);
}
@ModuleGen(name = "database", groupPackage = "net.bytle.api.db")
package net.bytle.api.db;
import io.vertx.codegen.annotations.ModuleGen;
In the start method of a verticle, you will use the static factory create method of the interface to deploy your service (ie 1 service = 1 verticle)
SomeServiceInterface.create(argument, ready -> {
if (ready.succeeded()) {
// ready result is the implementation (ie SomeServiceInterfaceImpl instance)
ServiceBinder binder = new ServiceBinder(vertx).setAddress(IP_QUEUE_NAME);
binder.register(SomeServiceInterface.class, ready.result());
promise.complete();
} else {
promise.fail(ready.cause());
}
});
The proxy is used in every server call to the service.
SomeDatabaseService service = SomeDatabaseService.createProxy(vertx,
"database-service-address");
// Save some data in the database - this time using the proxy
service.doSomething(Arguments ..., res2 -> {
if (res2.succeeded()) {
// done
}
});
In a unit, you would:
@Before
public void prepare(TestContext context) throws InterruptedException {
vertx = Vertx.vertx();
JsonObject conf = new JsonObject()
.put(DatabaseVerticle.KEY_JDBC_URL, "jdbc:hsqldb:mem:testdb;shutdown=true")
.put(DatabaseVerticle.KEY_JDBC_MAX_POOL_SIZE, 4);
vertx.deployVerticle(
new DatabaseVerticle(), // The service
new DeploymentOptions().setConfig(conf),
context.asyncAssertSuccess(
id -> {
// the deployment was successful, you get the deployment id (no needed)
// but you can instantiate your service proxy
service = DatabaseServiceInterface.createProxy(vertx, DatabaseVerticle.IP_QUEUE_NAME)) // The service proxy
}
);
}