Presumably you already have heard for Vert.x  project, a tool-kit for building reactive applications on the JVM. Vert.x is completely asynchronous, lightweight, increadibly fast, event driven and definitely very promising project.  Some of you may have heard about Fibers, otherwise here and here some information can be found. Though the coolest is that in addition to Vertx non blocking and event driven toolkit, we can take advantage of Vertx-Sync as a component through which we can easy escape from well known callback hell while using non blocking APIs. Vertx-Sync allows you to perform asynchronous operations and receive events in a synchronous way, but without blocking kernel threads as a result of that even if  it’s synchronous is negligibly less performant. Vertx-Sync uses Quasar for providing its fibers.
In this article I’ll show you how to:
- create a simple Vertx project
- write simple and concise code via Vertx-Sync Â
- create simple REST API via Vertx-Web
- get some web content using HTTP client Â
- use MongoDB as an example for using of database from Vertx.
You can find a link for Github here. For the sake of this blog we need :
- JDK 1.8
- Maven
- MongoDB
Install and set up Mongo
Download the latest version of MongoDB  and unzip it to your local directory. You may download it from here. Then, make the following directory  /data/db inside in directory where MongoDB is unzipped.
To start server go to {mongo_directory}/bin and start mongod.
Now it is time to run mongo client. Go to {mongo_directory}/bin and start mongo .
Create testDB database and Entities collection
use testDB ; db.createCollection('Entities');
Create Vertx simple project.
It’s time now to create our Vertx project. Let’s do it! Our start point of the project is:
import io.vertx.core.Vertx; import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; public class Launcher { private static final Logger logger = LoggerFactory.getLogger(MainVerticle.class); public static void main(String[] args) { Vertx.vertx().deployVerticle(MainVerticle.class.getName(), h -> { if (h.succeeded()) { logger.info("Success: {0}", h.result()); } else { logger.error("Something went wrong: {0}", h.cause()); } }); } }
- And our Vertical :
import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.client.WebClientOptions; import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.ErrorHandler; import java.util.List; public class MainVerticle extends SyncVerticle { private static final Logger logger = LoggerFactory.getLogger(MainVerticle.class); private static final String COLLECTION_NAME= "Entities"; private WebClient webClient; private MongoClient mongoClient; @Override @Suspendable public void start(Future<Void> startFuture) throws Exception { super.start(startFuture); HttpServer server = vertx.createHttpServer(); Router router = Router.router(vertx); // enable BodyHandler globally for easiness of body accessing router.route().handler(BodyHandler.create()).failureHandler(ErrorHandler.create()); router.route(HttpMethod.GET, "/getWebContent").handler(Sync.fiberHandler(this::getWebContent)); router.route(HttpMethod.GET, "/entities").handler(Sync.fiberHandler(this::getAllEntities)); router.route(HttpMethod.GET, "/entities/:id").handler(Sync.fiberHandler(this::getEntityById)); router.route(HttpMethod.PUT, "/entities").handler(Sync.fiberHandler(this::saveNewEntity)); // HttpServer will be automatically shared if port matches server.requestHandler(router::accept).listen(8089); webClient = WebClient.create(vertx, new WebClientOptions().setSsl(true)); mongoClient = MongoClient.createShared (vertx, new JsonObject().put("connection_string", "mongodb://127.0.0.1:27017/testDb")); } @Suspendable private void saveNewEntity(RoutingContext routingContext){ final String response = Sync.awaitResult (h -> mongoClient.save(COLLECTION_NAME, routingContext.getBodyAsJson(), h)); routingContext.response().end(response); } @Suspendable private void getAllEntities(RoutingContext routingContext){ final List<JsonObject> entities = Sync.awaitResult (h -> mongoClient.find(COLLECTION_NAME, new JsonObject(), h)); routingContext.response().end(Json.encodePrettily(entities)); } @Suspendable private void getEntityById(RoutingContext routingContext){ final JsonObject query = new JsonObject() .put("_id", routingContext.pathParam("id")); final List<JsonObject> entity = Sync.awaitResult (h -> mongoClient.find(COLLECTION_NAME, query, h)); routingContext.response() .end(Json.encodePrettily(entity)); } @Suspendable private void getWebContent(RoutingContext routingContext){ final HttpResponse<Buffer> response = Sync.awaitResult (h -> webClient.getAbs("https://www.google.com").send(h)); final String responseContent = response.bodyAsString("UTF-8"); logger.info("Result of Http request: {0}", responseContent); routingContext.response() .putHeader(HttpHeaderNames.CONTENT_TYPE, "text/html") .end(responseContent); } }
- Launcher class is just for starting of our program and deploying of our MainVerticle.
- It has to be considered that our MainVerticle extends io.vertx.ext.sync.SyncVerticle. If it is not deployed as SyncVerticle instance fibers will not work.
- Create HttpServer and router for handling of our REST API ( following the next 3 steps):
- GET https://localhost:8089/getWebContent // request to “https://www.google.com” just for testing of async Http client.
- GET https://localhost:8089/entities
- GET https://localhost:8089/entities/:id  //:id is path parameter for finding of some entity
- PUT https://localhost:8089/entities // Create a new entity record. As request body Json as payload should be applied for example : {“name”: “Charlie Harper”}
- Create WebClient and MongoClient and use their async APIs synchronously via fibers.
- It is very important to use Sync.fiberHandler() when we need to use any handlers. So for usage of fibers in a normal handler, e.g. in the request handler of an Http server we must first convert the normal handler to a fiber handler.
- To get response from async APIs that return AsyncResult<T> Â as callback is very easy with Sync.awaitResul() . Below is some copied documentation from Vertx about it:
The documentation says the following : Many async operations in Vert.x-land take a Handler<AsyncResult<T>> as the last argument. An example would be executing a find using the Vert.x Mongo client or sending an event bus message and getting a reply.
Vertx-sync allows you to get the result of a one-shot asynchronous operation in a synchronous way.
This is done by using the Sync.awaitResult method.
The method is executed specifying the asynchronous operation that you want to execute in the form of a Consumer, the consumer is passed the handler at run-time. Using Lambda expression is even easier:
final List<JsonObject> entity = Sync.awaitResult (h -> mongoClient.find(COLLECTION_NAME, query, h)); final HttpResponse<Buffer> response = Sync.awaitResult (h -> webClient.getAbs("https://www.google.com").send(h));
7. The most important thing is to instrument our bytecode. Vertx use Quasar and their fiber implementation use bytecode instrumentation. We don’t need to do anything special but just have to start JVM with javaagent as VM option. For example: -javaagent:/path/to/quasar/core/quasar-core.jar. For that case we use “exec-maven-plugin” . That is content of pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns_xsi="https://www.w3.org/2001/XMLSchema-instance" xsi_schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.idle.fibers</groupId> <artifactId>vertx-sync-fibers</artifactId> <version>1.0-SNAPSHOT</version> <properties> <vertx.version>3.4.1</vertx.version> <main.verticle>org.idle.easy.fibers.MainVerticle</main.verticle> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-dependencies</artifactId> <version>${vertx.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web</artifactId> <version>${vertx.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-client</artifactId> <version>${vertx.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-auth-oauth2</artifactId> <version>${vertx.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-sync</artifactId> <version>${vertx.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-mongo-client</artifactId> <version>${vertx.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.0.0</version> <configuration> <filesets> <fileset> <directory>${project.basedir}/src/main/generated</directory> </fileset> </filesets> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> <configuration> <skip>true</skip> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.6.0</version> <configuration> <mainClass>org.idle.easy.fibers.Launcher</mainClass> <workingDirectory>target/classes</workingDirectory> <executable>java</executable> <arguments> <argument>-javaagent:${co.paralleluniverse:quasar-core:jar}</argument> </arguments> </configuration> </plugin> </plugins> </build> </project>
8. Finally to start our program just type : mvn clean package exec:java
And that is all 🙂