Rick

Rick
Rick

Saturday, February 27, 2016

Step 5 Using Vertx Service Proxies

When you compose a vert.x application, you may want to divvy your applications into services. Using the event bus to do this, forces us to write tons of boilerplate code. In Vertx 3, you can instead use service proxies, which allow you to create code that looks more like a service and less like tons of callback registry with the event bus. This is the main reason they created Vertx service proxies so that you could easily create services that were invokable via the event bus that can be exposed to other JVM languages, browsers or node.js. You can read more about that in the vert.x docs on service proxies.
With Verx Service proxies, you define a Java interface containing methods following the async pattern using Vertx Handler interface, and the common types which are listed on the home page of our wiki and in the vertx docs.
Vertx will use the event bus to invoke our service and get the response back. Vertx will also code generate clients in whatever language you want. All you have to do is include the corresponding artifact vertx-lang, and Vertx will generated the client stubs for that language.
Supported languages:
  • vertx-lang-ceylon Ceylon (supported by vertx team)
  • vertx-lang-ruby Ruby (supported by vertx team)
  • vertx-lang-js JavaScript (supported by vertx team)
  • vertx-lang-groovy Groovy (supported by vertx team)
  • vertx-lang-java Java (supported by vertx team)
  • vertx-lang-jruby JRuby (supported by vertx team)
  • Kotlin support
  • More languages are listed Vertx awesome

Adding service proxy

Add new dependencies to build.gradle

dependencies {
    compile "io.vertx:vertx-core:3.2.0"
    compile 'ch.qos.logback:logback-core:1.1.3'
    compile 'ch.qos.logback:logback-classic:1.1.3'
    compile 'org.slf4j:slf4j-api:1.7.12'
    compile 'org.jetbrains.kotlin:kotlin-stdlib:1.0.0-beta-4584'
    compile 'io.vertx:vertx-tcp-eventbus-bridge:3.2.0'
    compile 'io.vertx:vertx-web:3.2.0'
    compile 'io.vertx:vertx-service-proxy:3.2.0' //NEW Vertx Proxy code
    compile 'io.vertx:vertx-lang-js:3.2.0'       //NEW FOR JS code gen
    compile 'io.vertx:vertx-codegen:3.2.0'       //NEW Vertx code gen
    testCompile group: 'junit', name: 'junit', version: '4.11'
}
Next we need to add the vertx proxy generation support to gradle.

Add new sourceset for generated code to build.gradle

sourceSets {
    generated{
        java.srcDir "${projectDir}/src/generated/java"
    }
}

Create new gradle task to generate Java proxy code to build.gradle

task generateProxies(type: JavaCompile, group: 'build', 
             description: 'Generates the Vertx proxies') {
    source = sourceSets.main.java // input source set
    classpath = configurations.compile //+ configurations.vertx // add processor module to classpath
    // specify javac arguments
    options.compilerArgs = [
            "-proc:only",
            "-processor", "io.vertx.codegen.CodeGenProcessor", // vertx processor here
            "-AoutputDirectory=${projectDir}/src/main"
    ]
    // specify output of generated code
    destinationDir = file("${projectDir}/src/generated/java")
}

Update the compileJava built-in task to call generateProxies task in build.gradle

compileJava{
    dependsOn(generateProxies)
    source    += sourceSets.generated.java
    // specify javac arguments
    options.compilerArgs = [
            "-Acodetrans.output=${projectDir}/src/main"
    ]
}
In the compileJava, we added compilerArgs which will kick off the JavaScript code generation. The generateProxies creates the Java source files that we need for the service proxy, and the compileJava task generates the JavaScript that we need for the browser andnode.js. (I think.)

HelloWorldServiceInterface.java The interface to our service.

package com.github.vertx.node.example;


import io.vertx.codegen.annotations.ProxyClose;
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.serviceproxy.ProxyHelper;

@ProxyGen
@VertxGen
public interface HelloWorldServiceInterface {



    /**
     * Create a HelloWorldServiceInterface implementation.
     * @param vertx vertx
     * @return HelloWorldServiceInterface
     */
    static HelloWorldServiceInterface create(final Vertx vertx) {
        return new HelloWorldServiceImpl();
    }


    static HelloWorldServiceInterface createProxy(final Vertx vertx,
                                                  final String address) {
        return ProxyHelper.createProxy(HelloWorldServiceInterface.class, vertx, address);

    }

    // Actual service operations here...
    void hello(final String message,
               final Handler<AsyncResult<String>> resultHandler);


    @ProxyClose
    void close();
}
Notice the @ProxyGen and @VertxGen, these are used to generated client code and server binding code for this interface. The @ProxyClose is used to denote that a call to this method will close the remote connection to the event bus.
For the code generation to work you need a package-info.java as follows:

package-info.java

@ModuleGen(groupPackage = "com.github.vertx.node.example", name = "hello-module")
package com.github.vertx.node.example;
import io.vertx.codegen.annotations.ModuleGen;
The @ModuleGen annotation specifies group package and the name of the artifact.
Notice that we have to specify the module name and the group.
The implementation of the HelloWorldService is as follows:

HelloWorldServiceInterface

package com.github.vertx.node.example;

import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;


public class HelloWorldServiceImpl implements HelloWorldServiceInterface {

    @Override
    public void hello(final String message, final Handler<AsyncResult<String>> resultHandler) {

        resultHandler.handle(Future.succeededFuture("Hello World! " + message));

    }

    @Override
    public void close() {

    }
}
At this point the service is really simple. Refer to the vert.x docs on service proxies to see all of the supported types.

HelloWorldServiceImpl.java implementation of our service

package com.github.vertx.node.example;

import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;


public class HelloWorldServiceImpl implements HelloWorldServiceInterface {

    @Override
    public void hello(final String message, final Handler<AsyncResult<String>> resultHandler) {

        resultHandler.handle(Future.succeededFuture("Hello World! " + message));

    }

    @Override
    public void close() {

    }
}
The class HelloWorldServiceImpl does not have a lot of boiler plate code that you would usually expect from using the event bus. The boiler plate code is still there but it is in the generated classes, which we will show at the end.

Register and use the HellowWorldService

Next we want to register and use the HelloWorldService as follows.

Define an address - WebVerticle.java

...
public class WebVerticle extends AbstractVerticle {

    public static final String HELLO_WORLD_SERVICE = "hello.world";
    private HelloWorldServiceInterface helloWorldServiceInterface;

Create and register the implementation - WebVerticle.java

...
public class WebVerticle extends AbstractVerticle {
    ...

    @Override
    public void start() {

        /* Create the hello world service. */
        final HelloWorldServiceImpl helloWorldService = new HelloWorldServiceImpl();

        /* Register the proxy implementation. */
        ProxyHelper.registerService(HelloWorldServiceInterface.class, vertx,
                helloWorldService, HELLO_WORLD_SERVICE);
The call to ProxyHelper.registerService register this service implementation under the address HELLO_WORLD_SERVICE, i.e., "hello.world".

Create the proxy interface - WebVerticle.java

...
public class WebVerticle extends AbstractVerticle {
    ...

    @Override
    public void start() {
        ...
        /* Create the proxy interface to HelloWorldService. */
        helloWorldServiceInterface = ProxyHelper.createProxy(HelloWorldServiceInterface.class, vertx,
                HELLO_WORLD_SERVICE);
The above creates a proxy to the service. When you call methods on this proxy, it will put messages on the event bus that correspond to method calls on the HelloWorldService.

Register the new handler - WebVerticle.java

...
public class WebVerticle extends AbstractVerticle {
   ...

    @Override
    public void start() {
        ...

        /* Register a new handler that uses the proxy. */
        router.route("/hello-world/*").handler(event -> 
                 handleCallToHelloWorldProxy(event.request()));

Open up the event bus to the event bus bridge using the address - WebVerticle.java

...
public class WebVerticle extends AbstractVerticle {
    ...

    @Override
    public void start() {
        ...

        /* Allow Hello World service to be exposed to Node.js.
         * Also add Add the new Hello World proxy. */
        final BridgeOptions options = new BridgeOptions()
                ...
                .addInboundPermitted(new PermittedOptions().setAddress(HELLO_WORLD_SERVICE))
                .addOutboundPermitted(new PermittedOptions().setAddress(HELLO_WORLD_SERVICE));
The above allows inbound and outbound communication with the HelloWorldService although at this point, we are just implementing inbound.

Call the service proxy interface from the new handler - WebVerticle.java

...
public class WebVerticle extends AbstractVerticle {
    ...

    /**
     * Handle call to hello world service proxy from REST
     * @param httpRequest httpRequest
     */
    private void handleCallToHelloWorldProxy(final HttpServerRequest httpRequest) {

        /** Call the service proxy interface for Hello World. */
        helloWorldServiceInterface.hello(httpRequest.getParam("msg"), response -> {
            if (response.succeeded()) {
                logger.debug("Successfully invoked HelloWorldService Proxy to service {}", 
                             response.result());
                httpRequest.response().end(response.result());
            } else {

                logger.error("Can't send message to hello world service", 
                             response.cause());
                //noinspection ThrowableResultOfMethodCallIgnored
                httpRequest.response().setStatusCode(500).end(
                                             response.cause().getMessage());
            }
        });
    }
Here is the whole class.

WebVerticle.java

package com.github.vertx.node.example;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
import io.vertx.ext.web.handler.sockjs.PermittedOptions;
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
import io.vertx.serviceproxy.ProxyHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This gets started by the MainVerticle.
 * It configures the event bus bridge and install the REST routes.
 */
public class WebVerticle extends AbstractVerticle {

    public static final String HELLO_WORLD_SERVICE = "hello.world";
    private final Logger logger = LoggerFactory.getLogger(WebVerticle.class);


    private HelloWorldServiceInterface helloWorldServiceInterface;

    @Override
    public void start() {


        /* Create the hello world service. */
        final HelloWorldServiceImpl helloWorldService = new HelloWorldServiceImpl();

        /* Register the proxy implementation. */
        ProxyHelper.registerService(HelloWorldServiceInterface.class, vertx,
                helloWorldService, HELLO_WORLD_SERVICE);

        /* Create vertx web router. */
        final Router router = Router.router(vertx);

        /* Create the proxy interface to HelloWorldService. */
        helloWorldServiceInterface = ProxyHelper.createProxy(HelloWorldServiceInterface.class, vertx,
                HELLO_WORLD_SERVICE);


        /* Install our original "REST" handler at the /hello/ uri. */
        router.route("/hello/*").handler(event -> handleHttpRequestToHelloWorld(event.request()));

        /* Register a new handler that uses the proxy. */
        router.route("/hello-world/*").handler(event -> handleCallToHelloWorldProxy(event.request()));



        /* Allow Hello World service to be exposed to Node.js.
         * Also add Add the new Hello World proxy. */
        final BridgeOptions options = new BridgeOptions()
                .addInboundPermitted(
                        new PermittedOptions().setAddress(Services.HELLO_WORLD.toString()))
                .addInboundPermitted(new PermittedOptions().setAddress(HELLO_WORLD_SERVICE))
                .addOutboundPermitted(new PermittedOptions().setAddress(HELLO_WORLD_SERVICE));

        /* Configure bridge at this HTTP/WebSocket URI. */
        router.route("/eventbus/*").handler(SockJSHandler.create(vertx).bridge(options));

        /* Install router into vertx. */
        vertx.createHttpServer()
                .requestHandler(router::accept)
                .listen(8080);
    }

    /**
     * Handle call to hello world service proxy from REST
     * @param httpRequest httpRequest
     */
    private void handleCallToHelloWorldProxy(final HttpServerRequest httpRequest) {

        /** Call the service proxy interface for Hello World. */
        helloWorldServiceInterface.hello(httpRequest.getParam("msg"), response -> {
            if (response.succeeded()) {
                logger.debug("Successfully invoked HelloWorldService Proxy to service {}", response.result());
                httpRequest.response().end(response.result());
            } else {

                logger.error("Can't send message to hello world service", response.cause());
                //noinspection ThrowableResultOfMethodCallIgnored
                httpRequest.response().setStatusCode(500).end(response.cause().getMessage());
            }
        });
    }

    /** This REST endpoint if for hello.
     *  It invokes the hello world service via the event bus.
     * @param httpRequest HTTP request from vertx.
     */
    private void handleHttpRequestToHelloWorld(final HttpServerRequest httpRequest) {

        /* Invoke using the event bus. */
        vertx.eventBus().send(Services.HELLO_WORLD.toString(),
                HelloWorldOperations.SAY_HELLO_WORLD.toString(), response -> {

           /* If the response was successful, this means we were able to execute the operation on
              the HelloWorld service.
              Pass the results to the http request's response.
           */
            if (response.succeeded()) {
                /* Send the result to the http connection. */
                logger.debug("Successfully invoked HelloWorld service {}", response.result().body());
                httpRequest.response().end(response.result().body().toString());
            } else {
                logger.error("Can't send message to hello world service", response.cause());
                //noinspection ThrowableResultOfMethodCallIgnored
                httpRequest.response().setStatusCode(500).end(response.cause().getMessage());
            }
        });
    }
}

Code generation for Java

The generateProxies task that we defined gets used to generate the Java client side and Java server side bindings for our service. The code gets put into ${projectDir}/src/generated/javawhich we also add to our classpath so that it gets included with our fat jar.

HelloWorldServiceInterfaceVertxEBProxy.java - GENERATED CODE

/*
* Copyright 2014 Red Hat, Inc.
*
* Red Hat licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

package com.github.vertx.node.example;

import com.github.vertx.node.example.HelloWorldServiceInterface;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.Vertx;
import io.vertx.core.Future;
import io.vertx.core.json.JsonObject;
import io.vertx.core.json.JsonArray;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.function.Function;
import io.vertx.serviceproxy.ProxyHelper;
import io.vertx.core.Vertx;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import com.github.vertx.node.example.HelloWorldServiceInterface;

/*
  Generated Proxy code - DO NOT EDIT
  @author Roger the Robot
*/
public class HelloWorldServiceInterfaceVertxEBProxy implements HelloWorldServiceInterface {

  private Vertx _vertx;
  private String _address;
  private DeliveryOptions _options;
  private boolean closed;

  public HelloWorldServiceInterfaceVertxEBProxy(Vertx vertx, String address) {
    this(vertx, address, null);
  }

  public HelloWorldServiceInterfaceVertxEBProxy(Vertx vertx, String address, DeliveryOptions options) {
    this._vertx = vertx;
    this._address = address;
    this._options = options;
  }

  public void hello(String message, Handler<AsyncResult<String>> resultHandler) {
    if (closed) {
      resultHandler.handle(Future.failedFuture(new IllegalStateException("Proxy is closed")));
      return;
    }
    JsonObject _json = new JsonObject();
    _json.put("message", message);
    DeliveryOptions _deliveryOptions = (_options != null) ? new DeliveryOptions(_options) : new DeliveryOptions();
    _deliveryOptions.addHeader("action", "hello");
    _vertx.eventBus().<String>send(_address, _json, _deliveryOptions, res -> {
      if (res.failed()) {
        resultHandler.handle(Future.failedFuture(res.cause()));
      } else {
        resultHandler.handle(Future.succeededFuture(res.result().body()));
      }
    });
  }

  public void close() {
    if (closed) {
      throw new IllegalStateException("Proxy is closed");
    }
    closed = true;
    JsonObject _json = new JsonObject();
    DeliveryOptions _deliveryOptions = (_options != null) ? new DeliveryOptions(_options) : new DeliveryOptions();
    _deliveryOptions.addHeader("action", "close");
    _vertx.eventBus().send(_address, _json, _deliveryOptions);
  }


  private List<Character> convertToListChar(JsonArray arr) {
    List<Character> list = new ArrayList<>();
    for (Object obj: arr) {
      Integer jobj = (Integer)obj;
      list.add((char)(int)jobj);
    }
    return list;
  }

  private Set<Character> convertToSetChar(JsonArray arr) {
    Set<Character> set = new HashSet<>();
    for (Object obj: arr) {
      Integer jobj = (Integer)obj;
      set.add((char)(int)jobj);
    }
    return set;
  }

  private <T> Map<String, T> convertMap(Map map) {
    if (map.isEmpty()) { 
      return (Map<String, T>) map; 
    } 

    Object elem = map.values().stream().findFirst().get(); 
    if (!(elem instanceof Map) && !(elem instanceof List)) { 
      return (Map<String, T>) map; 
    } else { 
      Function<Object, T> converter; 
      if (elem instanceof List) { 
        converter = object -> (T) new JsonArray((List) object); 
      } else { 
        converter = object -> (T) new JsonObject((Map) object); 
      } 
      return ((Map<String, T>) map).entrySet() 
       .stream() 
       .collect(Collectors.toMap(Map.Entry::getKey, converter::apply)); 
    } 
  }
  private <T> List<T> convertList(List list) {
    if (list.isEmpty()) { 
          return (List<T>) list; 
        } 

    Object elem = list.get(0); 
    if (!(elem instanceof Map) && !(elem instanceof List)) { 
      return (List<T>) list; 
    } else { 
      Function<Object, T> converter; 
      if (elem instanceof List) { 
        converter = object -> (T) new JsonArray((List) object); 
      } else { 
        converter = object -> (T) new JsonObject((Map) object); 
      } 
      return (List<T>) list.stream().map(converter).collect(Collectors.toList()); 
    } 
  }
  private <T> Set<T> convertSet(List list) {
    return new HashSet<T>(convertList(list));
  }
}

HelloWorldServiceInterfaceVertxProxyHandler.java - GENERATED CODE

/*
* Copyright 2014 Red Hat, Inc.
*
* Red Hat licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

package com.github.vertx.node.example;

import com.github.vertx.node.example.HelloWorldServiceInterface;
import io.vertx.core.Vertx;
import io.vertx.core.Handler;
import io.vertx.core.AsyncResult;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.Message;
import io.vertx.core.eventbus.MessageConsumer;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.eventbus.ReplyException;
import io.vertx.core.json.JsonObject;
import io.vertx.core.json.JsonArray;
import java.util.Collection;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import io.vertx.serviceproxy.ProxyHelper;
import io.vertx.serviceproxy.ProxyHandler;
import io.vertx.core.Vertx;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import com.github.vertx.node.example.HelloWorldServiceInterface;

/*
  Generated Proxy code - DO NOT EDIT
  @author Roger the Robot
*/
public class HelloWorldServiceInterfaceVertxProxyHandler extends ProxyHandler {

  public static final long DEFAULT_CONNECTION_TIMEOUT = 5 * 60; // 5 minutes 

  private final Vertx vertx;
  private final HelloWorldServiceInterface service;
  private final long timerID;
  private long lastAccessed;
  private final long timeoutSeconds;

  public HelloWorldServiceInterfaceVertxProxyHandler(Vertx vertx, HelloWorldServiceInterface service) {
    this(vertx, service, DEFAULT_CONNECTION_TIMEOUT);
  }

  public HelloWorldServiceInterfaceVertxProxyHandler(Vertx vertx, HelloWorldServiceInterface service, long timeoutInSecond) {
    this(vertx, service, true, timeoutInSecond);
  }

  public HelloWorldServiceInterfaceVertxProxyHandler(Vertx vertx, HelloWorldServiceInterface service, boolean topLevel, long timeoutSeconds) {
    this.vertx = vertx;
    this.service = service;
    this.timeoutSeconds = timeoutSeconds;
    if (timeoutSeconds != -1 && !topLevel) {
      long period = timeoutSeconds * 1000 / 2;
      if (period > 10000) {
        period = 10000;
      }
      this.timerID = vertx.setPeriodic(period, this::checkTimedOut);
    } else {
      this.timerID = -1;
    }
    accessed();
  }

  public MessageConsumer<JsonObject> registerHandler(String address) {
    MessageConsumer<JsonObject> consumer = vertx.eventBus().<JsonObject>consumer(address).handler(this);
    this.setConsumer(consumer);
    return consumer;
  }

  private void checkTimedOut(long id) {
    long now = System.nanoTime();
    if (now - lastAccessed > timeoutSeconds * 1000000000) {
      service.close();
      close();
    }
  }

  @Override
  public void close() {
    if (timerID != -1) {
      vertx.cancelTimer(timerID);
    }
    super.close();
  }

  private void accessed() {
    this.lastAccessed = System.nanoTime();
  }

  public void handle(Message<JsonObject> msg) {
    try {
      JsonObject json = msg.body();
      String action = msg.headers().get("action");
      if (action == null) {
        throw new IllegalStateException("action not specified");
      }
      accessed();
      switch (action) {


        case "hello": {
          service.hello((java.lang.String)json.getValue("message"), createHandler(msg));
          break;
        }
        case "close": {
          service.close();
          close();
          break;
        }
        default: {
          throw new IllegalStateException("Invalid action: " + action);
        }
      }
    } catch (Throwable t) {
      msg.fail(-1, t.getMessage());
      throw t;
    }
  }

  private <T> Handler<AsyncResult<T>> createHandler(Message msg) {
    return res -> {
      if (res.failed()) {
        msg.fail(-1, res.cause().getMessage());
      } else {
        if (res.result() != null  && res.result().getClass().isEnum()) {          msg.reply(((Enum) res.result()).name());        } else {          msg.reply(res.result());        }      }
    };
  }

  private <T> Handler<AsyncResult<List<T>>> createListHandler(Message msg) {
    return res -> {
      if (res.failed()) {
        msg.fail(-1, res.cause().getMessage());
      } else {
        msg.reply(new JsonArray(res.result()));
      }
    };
  }

  private <T> Handler<AsyncResult<Set<T>>> createSetHandler(Message msg) {
    return res -> {
      if (res.failed()) {
        msg.fail(-1, res.cause().getMessage());
      } else {
        msg.reply(new JsonArray(new ArrayList<>(res.result())));
      }
    };
  }

  private Handler<AsyncResult<List<Character>>> createListCharHandler(Message msg) {
    return res -> {
      if (res.failed()) {
        msg.fail(-1, res.cause().getMessage());
      } else {
        JsonArray arr = new JsonArray();
        for (Character chr: res.result()) {
          arr.add((int) chr);
        }
        msg.reply(arr);
      }
    };
  }

  private Handler<AsyncResult<Set<Character>>> createSetCharHandler(Message msg) {
    return res -> {
      if (res.failed()) {
        msg.fail(-1, res.cause().getMessage());
      } else {
        JsonArray arr = new JsonArray();
        for (Character chr: res.result()) {
          arr.add((int) chr);
        }
        msg.reply(arr);
      }
    };
  }

  private <T> Map<String, T> convertMap(Map map) {
    return (Map<String, T>)map;
  }

  private <T> List<T> convertList(List list) {
    return (List<T>)list;
  }

  private <T> Set<T> convertSet(List list) {
    return new HashSet<T>((List<T>)list);
  }
}

Code generation for JavaScript

The compileJava generates the JavaScript for this example when we specify "-Acodetrans.output=${projectDir}/src/main".

hello_world_service_interface.js - GENERATED CODE

/*
 * Copyright 2014 Red Hat, Inc.
 *
 * Red Hat licenses this file to you under the Apache License, version 2.0
 * (the "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

/** @module hello-module-js/hello_world_service_interface */
var utils = require('vertx-js/util/utils');
var Vertx = require('vertx-js/vertx');

var io = Packages.io;
var JsonObject = io.vertx.core.json.JsonObject;
var JHelloWorldServiceInterface = com.github.vertx.node.example.HelloWorldServiceInterface;

/**
 @class
*/
var HelloWorldServiceInterface = function(j_val) {

  var j_helloWorldServiceInterface = j_val;
  var that = this;

  /**

   @public
   @param message {string} 
   @param resultHandler {function} 
   */
  this.hello = function(message, resultHandler) {
    var __args = arguments;
    if (__args.length === 2 && typeof __args[0] === 'string' && typeof __args[1] === 'function') {
      j_helloWorldServiceInterface["hello(java.lang.String,io.vertx.core.Handler)"](message, function(ar) {
      if (ar.succeeded()) {
        resultHandler(ar.result(), null);
      } else {
        resultHandler(null, ar.cause());
      }
    });
    } else throw new TypeError('function invoked with invalid arguments');
  };

  /**

   @public

   */
  this.close = function() {
    var __args = arguments;
    if (__args.length === 0) {
      j_helloWorldServiceInterface["close()"]();
    } else throw new TypeError('function invoked with invalid arguments');
  };

  // A reference to the underlying Java delegate
  // NOTE! This is an internal API and must not be used in user code.
  // If you rely on this property your code is likely to break if we change it / remove it without warning.
  this._jdel = j_helloWorldServiceInterface;
};

/**
 Create a HelloWorldServiceInterface implementation.

 @memberof module:hello-module-js/hello_world_service_interface
 @param vertx {Vertx} vertx 
 @return {HelloWorldServiceInterface} HelloWorldServiceInterface
 */
HelloWorldServiceInterface.create = function(vertx) {
  var __args = arguments;
  if (__args.length === 1 && typeof __args[0] === 'object' && __args[0]._jdel) {
    return utils.convReturnVertxGen(JHelloWorldServiceInterface["create(io.vertx.core.Vertx)"](vertx._jdel), HelloWorldServiceInterface);
  } else throw new TypeError('function invoked with invalid arguments');
};

/**

 @memberof module:hello-module-js/hello_world_service_interface
 @param vertx {Vertx} 
 @param address {string} 
 @return {HelloWorldServiceInterface}
 */
HelloWorldServiceInterface.createProxy = function(vertx, address) {
  var __args = arguments;
  if (__args.length === 2 && typeof __args[0] === 'object' && __args[0]._jdel && typeof __args[1] === 'string') {
    return utils.convReturnVertxGen(JHelloWorldServiceInterface["createProxy(io.vertx.core.Vertx,java.lang.String)"](vertx._jdel, address), HelloWorldServiceInterface);
  } else throw new TypeError('function invoked with invalid arguments');
};

// We export the Constructor function
module.exports = HelloWorldServiceInterface;

hello_world_service_interface.js - GENERATED CODE

/*
 * Copyright 2014 Red Hat, Inc.
 *
 * Red Hat licenses this file to you under the Apache License, version 2.0
 * (the "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

/** @module hello-module-js/hello_world_service_interface */
!function (factory) {
  if (typeof require === 'function' && typeof module !== 'undefined') {
    factory();
  } else if (typeof define === 'function' && define.amd) {
    // AMD loader
    define('hello-module-js/hello_world_service_interface-proxy', [], factory);
  } else {
    // plain old include
    HelloWorldServiceInterface = factory();
  }
}(function () {

  /**
 @class
  */
  var HelloWorldServiceInterface = function(eb, address) {

    var j_eb = eb;
    var j_address = address;
    var closed = false;
    var that = this;
    var convCharCollection = function(coll) {
      var ret = [];
      for (var i = 0;i < coll.length;i++) {
        ret.push(String.fromCharCode(coll[i]));
      }
      return ret;
    };

    /**

     @public
     @param message {string} 
     @param resultHandler {function} 
     */
    this.hello = function(message, resultHandler) {
      var __args = arguments;
      if (__args.length === 2 && typeof __args[0] === 'string' && typeof __args[1] === 'function') {
        if (closed) {
          throw new Error('Proxy is closed');
        }
        j_eb.send(j_address, {"message":__args[0]}, {"action":"hello"}, function(err, result) { __args[1](err, result &&result.body); });
        return;
      } else throw new TypeError('function invoked with invalid arguments');
    };

    /**

     @public

     */
    this.close = function() {
      var __args = arguments;
      if (__args.length === 0) {
        if (closed) {
          throw new Error('Proxy is closed');
        }
        j_eb.send(j_address, {}, {"action":"close"});
        closed = true;
        return;
      } else throw new TypeError('function invoked with invalid arguments');
    };

  };

  /**
   Create a HelloWorldServiceInterface implementation.

   @memberof module:hello-module-js/hello_world_service_interface
   @param vertx {Vertx} vertx 
   @return {HelloWorldServiceInterface} HelloWorldServiceInterface
   */
  HelloWorldServiceInterface.create = function(vertx) {
    var __args = arguments;
    if (__args.length === 1 && typeof __args[0] === 'object' && __args[0]._jdel) {
      if (closed) {
        throw new Error('Proxy is closed');
      }
      j_eb.send(j_address, {"vertx":__args[0]}, {"action":"create"});
      return;
    } else throw new TypeError('function invoked with invalid arguments');
  };

  /**

   @memberof module:hello-module-js/hello_world_service_interface
   @param vertx {Vertx} 
   @param address {string} 
   @return {HelloWorldServiceInterface}
   */
  HelloWorldServiceInterface.createProxy = function(vertx, address) {
    var __args = arguments;
    if (__args.length === 2 && typeof __args[0] === 'object' && __args[0]._jdel && typeof __args[1] === 'string') {
      if (closed) {
        throw new Error('Proxy is closed');
      }
      j_eb.send(j_address, {"vertx":__args[0], "address":__args[1]}, {"action":"createProxy"});
      return;
    } else throw new TypeError('function invoked with invalid arguments');
  };

  if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && module.exports) {
      exports = module.exports = HelloWorldServiceInterface;
    } else {
      exports.HelloWorldServiceInterface = HelloWorldServiceInterface;
    }
  } else {
    return HelloWorldServiceInterface;
  }
});

Running this

To get the Java service proxy to run, run the application.

Run MainVerticle

$ gradle clean shadowJar
$ find . -name "*.jar"
$ java -jar ./build/libs/vertx-1.0-SNAPSHOT-fat.jar
Now hit the REST end point that calls the HelloWorld service proxy through the HTTP end point.

Hit the REST end point which calls the HelloWorldService proxy

$ curl http://localhost:8080/hello-world/foo?msg=Rick
Hello World! Rick

1 comment:

  1. Thank you, Vertx official manual does not go as deep as you in the matter. Have a great day!

    ReplyDelete

Kafka and Cassandra support, training for AWS EC2 Cassandra 3.0 Training