In this blog post I would like to demonstrate an example on how you can implement a non-blocking web service, running on the JVM, on top of SAP HANA. This blog post, such as the commands and the setup, does assume running the backend on a Linux/Mac machine (bare metal or IaaS). The commands might slightly vary on Windows machines, but the experience should be similar.
"Vert.x is a tool-kit for building reactive applications on the JVM". This is what the Vert.x web site tells you.
Basically, Vert.x is an open source set of Java libraries, managed by the Eclipse foundation, that allows you to build event-driven and non-blocking applications. In case you are already familiar with Node.js, Vert.x allows you to build services the way you might already know from Node.js. Also, Vert.x is language-agnostic so you can implement your backend in your favorite JVM-based language, such as, but not limited to, Java, JavaScript, Groovy, Ruby, or Ceylon.
In case you want to want to know more about Vert.x, please refer to the official Vert.x web site or the official eclipse/vert.x repository on GitHub
Speaking in code, with Vert.x you can write a simple HTTP server and a web socket server like this (using Java 8):
vertx
.createHttpServer()
.requestHandler(req -> {
req.response().headers().set("Content-Type", "text/plain");
req.response().end("Hello World");
})
.websocketHandler(ws -> {
ws.writeFinalTextFrame("Hello World");
})
.listen(8080);
In case you want to know more about what makes a reactive application reactive, you can take a look at The Reactive Manifesto
Is that you? Think again! Depending on the use case, developing JVM-based backend services using Tomcat or a Java EE container such as JBoss might be the solution of choice for certain use cases, especially when it comes to transaction processing. For building real-time applications where you really don't care about transaction handling in the backend, using an application server might be an overkill for your project and much more than you actually needed.
Node.js is a great event-driven, non-blocking framework as well and the most popular amongst reactive backend frameworks and toolkits. I personally like Node.js a lot, simply because JavaScript itself is very flexible and npm.com has a really large ecosystem of Node.js packages. Also, there is great open-source HANA driver (SAP/node-hdb) for Node.js, so Node.js is still good choice for real-time applications.
However, Node.js has some pitfalls, especially when it comes to leveraging multiple CPU cores. There are also solutions in Node.js to address this problem. This blog post from Juanaid Anwar explains this really well: Taking Advantage of Multi-Processor Environments in Node.js
You find the complete, ready-to-run source code of the example on GitHub:
First, you need to create a Maven project. You can also use any other dependency manager or build tool (like Gradle), but this tutorial will use Maven.
For this example we will be using the following Vert.x libraries and the HANA JDBC driver:
<dependencies>
<!-- Vertx core -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>3.2.1</version>
</dependency>
<!-- Vertx web for RESTful web services -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>3.2.1</version>
</dependency>
<!-- Vertx async JDBC client -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-jdbc-client</artifactId>
<version>3.2.1</version>
</dependency>
<!-- HANA Driver -->
<dependency>
<groupId>com.sap.db</groupId>
<artifactId>com.sap.db.ngdbc</artifactId>
<version>1.00.38</version>
</dependency>
</dependencies>
<systemPath>${project.basedir}/src/main/resources/yourJar.jar</systemPath>
You really don't want to code in Vert.x below Java 8. You really don't. Since Vert.x heavily relies on callbacks, writing Vert.x without lambda expressions will be a pain.
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
...
</plugins>
</build>
In this example, we will build a single .jar file that will bootstrap our Vert.x code, which will contain all Java dependencies. There are many ways of deploying Verticles, this is just an example one.
Hereby, we will be referencing to com.github.mitchk.hana_vertx.example1.web.HANAVerticle as our main class.
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>io.vertx.core.Starter</Main-Class>
<Main-Verticle>com.github.mitchk.hana_vertx.example1.web.HANAVerticle</Main-Verticle>
</manifestEntries>
</transformer>
</transformers>
<artifactSet />
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar</outputFile>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
According to the official documentation, a "Verticle" is a Vert.x term that describes an independently deployable piece of code. Outside of the Vert.x universe, you may call it "micro service". The use of Verticles is entirely optional, but I will show how to implement an example HANA Verticle.
Create a new class in a package of your choice. Make sure that the package name and class name are matching the main Verticle class you put into the pom.xml.
public class HANAVerticle extends AbstractVerticle {
@Override
public void start(Future<Void> fut) {
vertx
.createHttpServer()
.requestHandler(req -> {
req.response().headers().set("Content-Type", "text/plain");
req.response().end("Hello World");
})
.websocketHandler(ws -> {
ws.writeFinalTextFrame("Hello World");
})
.listen(8080);
}
}
Now run
$ mvn clean install package
$ java -jar target/example1-0.0.1-SNAPSHOT-fat.jar
on your command line (or set up your IDE accordingly) in order to install your Maven dependencies and create a fat .jar file.
Finally, open http://localhost:8080/ in your web browser.
You can also check whether you web socket endpoint is listening on ws://localhost:8080, using a web socket client:
Let's now build a simple RESTful web service where we actually want to use routing and also JSON output. Replace the content of your start() method with:
Router router = Router.router(vertx);
router
.get("/api/helloWorld").handler(this::helloWorldHandler);
vertx.createHttpServer()
.requestHandler(router::accept)
.listen(
// Retrieve the port from the configuration,
// default to 8080.
config().getInteger("http.port", 8080), result -> {
if (result.succeeded()) {
fut.complete();
} else {
fut.fail(result.cause());
}
});
You also need to create the handler method for the /api/helloWorld end point:
public void helloWorldHandler(RoutingContext routingContext) {
JsonObject obj = new JsonObject();
obj.put("message", "Hello World");
routingContext
.response().setStatusCode(200)
.putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily(obj));
}
Build your code again, start the .jar file and see the result in the browser:
Now things become interesting. Put the following code snippet to the beginning of the start() method:
JsonObject config = new JsonObject();
// Example connection string "jdbc:sap://hostname:30015/?autocommit=false"
config.put("url", System.getenv("HANA_URL"));
config.put("driver_class", "com.sap.db.jdbc.Driver");
config.put("user", System.getenv("HANA_USER"));
config.put("password", System.getenv("HANA_PASSWORD"));
client = JDBCClient.createShared(vertx, config); // , "java:comp/env/jdbc/DefaultDB");
We will actually connect to HANA using environment variables for the configuration, for simplicity. You can also use a JNDI name instead.
In the helloWorldHandler, replace the method content with this code:
client.getConnection(res -> {
if (!res.succeeded()) {
System.err.println(res.cause());
JsonObject obj = new JsonObject();
obj.put("error", res.cause());
routingContext.response().setStatusCode(500)
.putHeader("content-type", "application/json; charset=utf-8").end(Json.encodePrettily(obj));
return;
}
SQLConnection connection = res.result();
connection.query("SELECT 'Hello World' AS GREETING FROM DUMMY", res2 -> {
if (!res2.succeeded()) {
System.err.println(res2.cause());
JsonObject obj = new JsonObject();
obj.put("error", res2.cause());
routingContext.response().setStatusCode(500)
.putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily(obj));
return;
}
ResultSet rs = res2.result();
routingContext
.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily(rs));
});
});
Now, build the code again. Before you execute the .jar file, make sure you set your environment variables accordingly in the shell.
$ export HANA_URL=jdbc:sap://<your host>:3<your instance id>15/?autocommit=false
$ export HANA_USER=<user>
$ export HANA_PASSWORD=<your password>
After you executed the jar file again, you can see your result in the browser:
Now you just developed your first Vert.x backend on top of SAP HANA!
Vert.x and SAP HANA work very well together, especially for real-time applications. If you want to develop your web services on top of JVM and want to avoid dealing with a servlet container or even a whole application server, Vert.x might be a great choice for you.
If you find any mistakes or have any feedback, please feel free to leave me a comment. :smile:
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
38 | |
19 | |
13 | |
13 | |
11 | |
10 | |
10 | |
10 | |
8 | |
8 |