Vert.x RESTful Services on Java.
Distributed Systems Development A-Z Guide.
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.
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.
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.
Corresponded Module settings are straightforward.
Don’t forget to enable Auto-Import.
So our umbrella package should look like this. We have a pom.xml file which contains all the required settings.
But before anything else let’s add .gitignore file to avoid garbage in the repo.
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.
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.
We have a modules tag. IDE will automatically add all underlying modules inside it.
Let’s add a dependency management tag to define common information for all io.vertx dependencies inside the project.
The first dependency is a vertx-core which is, well, Vert.x core.
The second is a Service Discovery dependency. We will use it to locate all deployed verticles inside the microservice.
In the last section of the config, we define the minimum deployment target of the JVM and it’s 9.
Now we’re ready to add Common Classes.
Common.
We add a new Maven module to the project. We’re calling it common.
Straightforward Maven module settings. Here we also define an umbrella project(root) of the module.
Its pom.xml is short. Here we have only automatically created tags that define only information about the package.
But in umbrella pom.xml we have a new submodule automatically added — common.
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.
Let’s take a look at MicroserviceVerticle. First of all, it extends from AbstractVerticle.
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.
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.
We use createServiceDiscovery() method at the beginning of the verticle’s lifecycle— 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.
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.
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.
Also, we should stop the Discovery Service. All we need to do is to close() it and send a completion message to the stopFuture.
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.
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.
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.
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.
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.
One more method is getConfig(). This is exactly where we read a JSON file.
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.
Module settings are the same as with Common.
And package info defines the module’s parent module and module ArtifactId.
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.
We have a single dependency here and it is the Common module.
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.
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.
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.
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.
ServerVerticle extending AbstractVerticle class.
Let’s define our demo user — a User object with Russian name Vasja Ivanov.
Here we override start() — a method that will be called when we start verticle. method and add createServer() method which we define below.
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.
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.
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.
The last class we define is a starting verticle for API Mobile service. It extends MicroserviceVerticle from Common module.
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.
In deployServer() method we define a verticle’s class name, its options and make a deploy via a deployVerticle method.
PublishEndpoint() method calls the publishHttpEndpoint method of MicroserviceVerticle with corresponded parameters. On the successful result, it calls publishHandler() method.
Up and Running.
Finally, we’re ready to run our microservice. Let’s add a new configuration.
Select Application from the list of available configurations.
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.
Start the newly created application. You should see Server Started in logs.
From the browser enter localhost:30000 and here you go.
We’re done. You can find the full source code at our Github repo.
Thank you for reading!