Vert.X 3 est arrivé son lot de nouveauté, en particulier, les services. Mais alors, comment qu’ça marche et à quoi qu’ça sert?

Les services de Vert.X permettent d’offrir une sorte de DAO accessible via l’eventBus. Vert.X va nous générer un stub proxy qui permettra d’interagir avec notre classe service sans avoir à se préoccuper de toute la couche de communication de l’eventBus (même si elle est très simple).

Pour commencer, nous avons à ajouter 2-3 bricoles dans notre script Gradle :

compile "io.vertx:vertx-service-proxy:3.2.1"
compile "io.vertx:vertx-codegen:3.2.1"

[...]

def generateSrcPath="$buildDir/generated-src"
def generatedSrcDir = file("$buildDir/generated-src")
repositories {
    jcenter()
}
sourceSets {
    main {
        java.srcDirs += generatedSrcDir
        output.dir(builtBy: 'generateServiceProxy', generateSrcPath)
    }
}
task generateServiceProxy(type: JavaCompile, description: 'Generates EBServiceProxies') {
    source = sourceSets.main.java
    classpath = configurations.
            compile
    destinationDir = generatedSrcDir
    options.compilerArgs = [    
            "-proc:only",
            "-processor", "io.vertx.codegen.CodeGenProcessor",    
            "-AoutputDirectory=$generateSrcPath"
    ]
}    
compileJava.dependsOn generateServiceProxy

Ensuite, nous allons créer notre contrat d’interface de service :

package org.giwi.vertx.service;

import io.vertx.codegen.annotations.ProxyGen;
import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonArray;
import io.vertx.serviceproxy.ProxyHelper;
import org.giwi.vertx.service.impl.BeerServiceImpl;

@ProxyGen
@VertxGen
public interface BeerService {
    // ça va être notre adresse sur le bus
    String ADDRESS = "vertx.beer.service";

    static BeerService create(Vertx vertx) {
        return new BeerServiceImpl(vertx);
    }

    static BeerService createProxy(Vertx vertx, String address) {
        return ProxyHelper.createProxy(BeerService.class, vertx, address);
    }

    // la méthode intéressante
    void getList(Handler<AsyncResult> resultHandler);
}

Notez les deux méthodes static, elles ont leur importance. La méthode getList prend en paramètre un handler (et oui, nous évoluons dans un monde asynchrone non bloquant), elle pourrait avoir des paramètres supplémentaires mais doit retourner un void.

Il faut créer un fichier package-info.java dans le même package contenant :

@ModuleGen(name = "giwi", groupPackage = "org.giwi.vertx.service")
package org.giwi.vertx.service;

import io.vertx.codegen.annotations.ModuleGen;

Notez ici le nom des packages, c’est très important d’avoir la cohérence entre le nom de package ou se trouve l’interface et l’annotation @ModuleGen.groupPackage.

Ensuite, nous devons implémenter notre service :

package org.giwi.vertx.service.impl;

import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.giwi.vertx.model.Beer;
import org.giwi.vertx.service.BeerService;

public class BeerServiceImpl implements BeerService {
    public BeerServiceImpl(Vertx vertx) {
        super();
    }

    public void getList(Handler<AsyncResult> resultHandler) {
        JsonArray list = new JsonArray();
        Beer beer = new Beer();
        beer.setAlcohol(6.8);
        beer.setDescription("Affligem Blonde, the classic clear blonde abbey ale, with a gentle roundness and 6.8% alcohol. Low on bitterness, it is eminently drinkable.");
        beer.setId("AffligemBlond");
        beer.setImg("beers/img/AffligemBlond.jpg");
        beer.setName("Affligem Blond");
        list.add(new JsonObject(Json.encode(beer)));
        // ...
        resultHandler.handle(Future.succeededFuture(list)); 
    }
}

Notez

resultHandler.handle(Future.succeededFuture(list));

permettant de transmettre le résultat à notre handler asynchrone (cf API).

Enfin, dans la classe principale de votre application, on déclare le service auprès de Vert.X et on peut l’utiliser à tout moment.

package org.giwi.vertx;

import io.vertx.core.Vertx;
import io.vertx.serviceproxy.ProxyHelper;
import org.giwi.vertx.service.BeerService;
import org.giwi.vertx.service.impl.BeerServiceImpl;

public class Main {

    public static void main(String... args) {
        Vertx vertx = Vertx.vertx();
        // On enregistre le service sur Vert.X
        ProxyHelper.registerService(Main.class, vertx, new BeerServiceImpl(vertx), BeerService.ADDRESS);
        vertx.deployVerticle(Server.class.getName(), res -> {
            if (res.succeeded()) {
                System.out.println("Started");
            } else {
                res.cause().printStackTrace();
            }
        });
    }

    @Override
    public void start() throws Exception {
        router.get("/*").handler(routingContext -> {
            routingContext.response()
                .putHeader("content-type", "application/json")
                .setChunked(true);
            routingContext.next();
        });
        router.get("/api/beer/").handler(this::beerList);   
    }

    private void beerList(RoutingContext routingContext) {
        // On récupère le service
        BeerService beerService = BeerService.createProxy(vertx, BeerService.ADDRESS);
        beerService.getList(asyncResult -> routingContext.response().end(asyncResult.result().encodePrettily()));
    }
}

Bon, ça ne va pas marcher tout seul, il faut quand même générer nos classes :

./gradlew clean build

En exécutant la classe Main, normalement, la magie opère.

Vous avez un exemple complet sur mon Github.