Vert.x RESTful Services on Java.

Distributed Systems Development A-Z Guide.

Dmytro Nasyrov
Pharos Production
10 min readFeb 13, 2019

--

Vert.x RESTful Service on Java.

Give us a message if you’re interested in Blockchain and FinTech software development or just say Hi at Pharos Production Inc.

Or follow us on Youtube to know more about Software Architecture, Distributed Systems, Blockchain, High-load Systems, Microservices, and Enterprise Design Patterns.

Pharos Production Youtube channel

In the previous article, we created Vert.x basic project using Scala and SBT.

This time we will create a one-route RESTful microservice on Vert.x but using Java. We’re going to build a toy microservice that will respond to us with a plain JSON on http://localhost:30000/users. So let’s begin our journey.

Project Setup.

We will use Maven to configure all related dependencies. But you can use Gradle, or SBT, or whatever.

Create a new Maven project.

The first Maven project will be an umbrella for all services inside User’s microservice. There will be two of them — the first one is a Common classes service. It will not be deployed but cover all common functionality. The second one will be API for mobile apps — this is exactly what we’re going to build in this tutorial. But also you can add more services to the umbrella.

Here we define GroupId which is com.pharosproduction and ArtifactId which is a microservice name — Users. The version is 1.0.

Umbrella setup.

Corresponded Module settings are straightforward.

Module settings.

Don’t forget to enable Auto-Import.

Auto-Import.

So our umbrella package should look like this. We have a pom.xml file which contains all the required settings.

Umbrella structure.

But before anything else let’s add .gitignore file to avoid garbage in the repo.

.gitignore

Let’s go through the Maven project configuration.

We can see here all entries from setup screens — GroupId, ArtifactId, Packaging is a pom, and a Version.

Package Info.

Also, we define properties to make a single version number for all Vert.x dependencies and to define a verticle that will be a starting point of the service. It will be overridden in every service.

Properties.

We have a modules tag. IDE will automatically add all underlying modules inside it.

Modules tag.

Let’s add a dependency management tag to define common information for all io.vertx dependencies inside the project.

Dependency Management.

The first dependency is a vertx-core which is, well, Vert.x core.

Vert.x core.

The second is a Service Discovery dependency. We will use it to locate all deployed verticles inside the microservice.

Service Discovery.

In the last section of the config, we define the minimum deployment target of the JVM and it’s 9.

Deployment target.

Now we’re ready to add Common Classes.

Common.

We add a new Maven module to the project. We’re calling it common.

New Maven module.

Straightforward Maven module settings. Here we also define an umbrella project(root) of the module.

Common module.

Its pom.xml is short. Here we have only automatically created tags that define only information about the package.

Common pom.xml.

But in umbrella pom.xml we have a new submodule automatically added — common.

A new submodule.

Let’s add two classes here — Launcher class and MicroserviceVerticle. Launcher — is a class nested from io.vertx.core.Launcher which is a starting point in any Vert.x application. MicroserviceVerticle is a set of methods that we will use to start, stop and deploy verticles and information about them.

Common module structure.

Let’s take a look at MicroserviceVerticle. First of all, it extends from AbstractVerticle.

MicroserviceVerticle definition.

It will contain two global variables. One of the is a ServiceDiscovery instance which we will use to discover our verticles. Another one is a set of Record objects. Record — this is how we wrap the information about every verticle for lookup and stopping them.

Discovery variables.

Let’s define a private method to create a ServiceDiscovery object. Here config is a JsonObject. Every verticle has a config() method. There are various settings in ServiceDiscoveryOptions class, but for us, it’s enough to use setBackendConfiguration.

Create ServiceDiscovery.

We use createServiceDiscovery() method at the beginning of the verticle’s lifecycle— start() method.

start() method.

To make a verticle discoverable we need to publish it into ServiceDiscovery. We use publish() method. If service discovery hasn’t been started, we start it. Next, we’re publishing a Record and waiting from a non-blocking Vert.x in a handler. A handle has a type AsyncResult<Record>. AsyncResult can succeed or fail. In case of success, we add the record to the set of records and call a completion handler — also AsyncResult but carrying type Void.

Publish method.

We will use publish() private method in the single public method of the class -publishHttpEndpoint. This method will create a new HttpEndpoint object with all required parameters and publish it into service discovery.

Publish endpoint method.

Another method we should add is an unpublish() method. This method unpublish a Record by calling unpublish method in Service Discovery and returning a future.

Unpublish method.

Also, we should stop the Discovery Service. All we need to do is to close() it and send a completion message to the stopFuture.

Stopping discovery service.

All these methods should be called from a stop() method which sits in every verticle. In two words — we mapping the list of registered Records to futures with asynchronous unpublish call and then we stop the Discovery Service.

Stop verticle.

Next class we have created is a Launcher class. This is an entry point into the service and we extend it from the default Vert.x entry point class io.vertx.core.Launcher.

Launcher class.

In this class, we define the main method. Launcher class is the entry point and it has a dispatch method that will dispatch verticle into JVM with arguments from plain old String[] args.

Main Launcher.

We define a path to config.json — file which will contain service configuration. Every service in the microservice will have the same location for the config file for the sake of clarity.

Config file location.

Here we override a method which will run before verticle deployment. In this method, we take deployment options. We call defaultOptions() method in case options are null. This method will create a new JsonObject instance for us. Then we read a configuration file and add remap it to our newly created or previously existed configuration JsonObject.

Read config.
Default Options.
Read the config file and merge with options.

One more method is getConfig(). This is exactly where we read a JSON file.

Read config JSON.

API Mobile.

We’re ready for API Mobile Service creation. We start absolutely the same as we have done with the Common Service. We create a new Maven module and call it api_mobile.

New api_mobile module.

Module settings are the same as with Common.

Module settings

And package info defines the module’s parent module and module ArtifactId.

Package Info.

It’s time to define the main.verticle property. This service is the first service inside the app that we will run as a separate application. And the class is ApiMobileVerticle.

Main.Verticle in api_mobile.

We have a single dependency here and it is the Common module.

Dependencies.

Let’s examine the module structure. We create 4 classes here:

  • Config — this class is a mapper between JSON file and Java Objects;
  • User — our model;
  • ApiMobileVerticle — verticle that we’re going to launch as a service.
  • ServerVerticle — verticle that will act as a server.

Also, don’t forget to create a conf directory and config.json inside it.

Module structure.

Config.json is very straightforward. Actually, you can replace it with whatever configuration format you like. Here in Pharos Production, we use TOML format actively, but it’s up to you. In config, we define localhost as a host and 30000 as a port.

config.json

Let’s look through User class. We have the first and last name here, constructor and getters are below. One more small thing that we have added are two annotations — JsonAutoDetect and JsonProperty — to convert POJO into JSON automagically.

User class.

Let’s take a look at the Config class. Here we define static names attributes inside JSON config and a default port 8080 in case port in the JSON is not found. In constructor, we get JsonObject of http attribute and take port and host from it. Also, we define which protocols we want to use — HTTP1.1 and HTTP2. Now we’re ready to create server settings from all this information by creating the object of type HttpServerOptions. Also, let’s define a getter for it.

Config class.

ServerVerticle extending AbstractVerticle class.

ServerVerticle class.

Let’s define our demo user — a User object with Russian name Vasja Ivanov.

User model.

Here we override start() — a method that will be called when we start verticle. method and add createServer() method which we define below.

start() method.

Let’s define createServer() method. In this method, we define a server configuration and starting the HTTP server with two functions passed into requestHandler() and listen() methods.

createServer() method.

The first handler will be called on every request. Here we will define the response which has Content-Type header with the value application/json. And the user encoded into a string and sent as a response.

handleRequest() method.

HandleListener() method is a result of the listen operation. It will notify us in case there is an error while server starting or everything is ok.

handleListener() method.

The last class we define is a starting verticle for API Mobile service. It extends MicroserviceVerticle from Common module.

ApiMobileVerticle definition.

Here we override start() method and call two functions inside:

  • deployServer — is a method which deploys server verticle into JVM;
  • publishEndpoint — is a method which registers verticle in Service Discovery.
start() method.

In deployServer() method we define a verticle’s class name, its options and make a deploy via a deployVerticle method.

deployServer() method.

PublishEndpoint() method calls the publishHttpEndpoint method of MicroserviceVerticle with corresponded parameters. On the successful result, it calls publishHandler() method.

publishEndpoint() method.

Up and Running.

Finally, we’re ready to run our microservice. Let’s add a new configuration.

Add new configuration.

Select Application from the list of available configurations.

Create a new Application.

Main class is com.pharosproduction.users.common.Launcher. And to run the program you should call your service verticle with a corresponded configuration file.

run com.pharosproduction.users.api_mobile.ApiMobileVerticle -conf api_mobile/conf/config.json

Don’t forget to select a module for classpath — api_mobile.

Application config.

Start the newly created application. You should see Server Started in logs.

Server started.

From the browser enter localhost:30000 and here you go.

The user is in JSON format.

We’re done. You can find the full source code at our Github repo.

Thank you for reading!

--

--

Dmytro Nasyrov
Pharos Production

We build high-load software. Pharos Production founder and CTO.