Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 

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.

What is Vert.x?


"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

"Building a Java web service on top of HANA? That requires running Tomcat."

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.

What about Node.js?

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

GitHub repository

You find the complete, ready-to-run source code of the example on GitHub:

GitHub - MitchK/hana_vertx_example: An example web service to demonstrate how to use Vert.x with SAP...

Example Preparation

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>


















  • vertx-core: Provides the basic Vert.x toolkit functionality
  • vertx-web: Provides you with routing capabilities to build RESTful web services.
  • vertx-jdbc-client: Provides you with an asynchronous JDBC Client and with a lot of convenient APIs on top of JDBC.
  • com.sap.db.ngdbc: The official SAP HANA driver for JDBC. This driver is not open source and thus not available on Maven Central. You either have to use your company's internal Nexus server or refer to the .jar file on the file system using your pom.xml using <systemPath>${project.basedir}/src/main/resources/yourJar.jar</systemPath>

Using Java 8


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>












Creating a fat .jar

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>

















Creating a Verticle


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:


Building a RESTful web service with Vert.x


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:



Connecting Vert.x with HANA


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!

Conclusion

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:


1 Comment