jLuger.de - Native x86 Microservice in Java: The Java program

This is the second part of a series about developing a native x86 microservice in Java. And it is about the creating of the microservice in Java.
As mentioned in the previous post I've wanted a fast startup time. Thus I've decided to not base the microservice on any technology that requires an application server.
First I've thought about using spring boot. But after some research I've found that they had some issues with GrallVM at that time. Then I've tried the Micronaut framework but couldn't get it running.

So I've started with the Spark Framework as someone else had used it successful with GraalVM. From there I've also took the GSON framwork to convert between Java Pojos and JSON.
For the database access I had two options: Plain JDBC or JPA. While the first sounds tedious the second requires a lot of jars which require too much initialization time for my project. While thinking about the options I've remembered the dropwizard project and that it had this strange data access framework named Jdbi. It turned out that Jdbi is a programmer friendly layer above JDBC.
The database to access was initially H2 db but I had to change to Hsqldb later. I will explain the reason for this in another post.

For the dependency injection I've relied on the good old constructor based dependency injection as it is free of charge (not only money but also runtime resources) and the disadvantages have no impact on such a small project.

Here is some sample code from my main class:
	AuthResource authResource = new AuthResource();
	Jdbi jdbi = Jdbi.create("jdbc:h2:./db/test");
	SuggestionDao suggestionDao = new SuggestionDao(jdbi);
	SuggestionResource suggestionResource = new SuggestionResource(suggestionDao);
	Spark.externalStaticFileLocation("web");
	Spark.get("/favicon.ico", (request,response)->"");
	Spark.path("/api", () -> {
		Spark.path("/login", ()-> {
			Spark.post("/desktop", authResource::login);
		});
		Spark.path("/protected", ()-> {
			Spark.path("/mobile", ()-> {
				Spark.get("/suggestions", suggestionResource::listTopSuggestions);
			});
			Spark.path("/desktop", ()-> {
				Spark.before("/*", authResource::filterDesktop);
				Spark.get("/suggestions", suggestionResource::listAllSuggestions);
				Spark.post("/suggestion", suggestionResource::updateSuggestion);
				Spark.delete("/suggestion/:id", suggestionResource::deleteSuggestions);
			});
		});
	});
This code starts a Jetty server on port 4567. Yes a Jetty server. Sparkjava is built by default on top of a Jetty server and the have chosen to use 4567 as default port. There is also no need to invoke a start method as soon as you use a http verb method of the Spark class.
The shown method calls ceate  e.g. the following rest endpoints:
/api/login/desktop
/api/protected/mobile/suggestions
...

When there is a file index.html in the web folder and someone requests /index.html Sparkjava will proivde the index.html file as a response.

Also not directly visible this code uses three functional interfaces of the Sparkajava framework: RouteGroup, Route and Filter. I think it is obvious where which one is used.
Thanks to the Java 8 method references you can create one class with several methods that implement the interface and are then used as interface implementation. The effect is that you can have resource classes like in Java EE but instead of annotations that need to be scanned and evaluated at startup time you assign the path mapping at compile time. And you need no fancy compile plugins for this.

As Jdbi is also a not so well known framework I want to show two methods to demonstrate how it is used:
	public List listAllSuggestions() {
		return jdbi.withHandle(handle -> {
			String sql = "SELECT ID,SUGGESTION FROM SUGGESTIONS ORDER BY SUGGESTION";
			return handle.createQuery(sql).mapToBean(Suggestion.class).list();
		});
	}

	public boolean updateSuggestion(Suggestion suggestion) {
		return jdbi.withHandle(handle -> {
			String sql = "UPDATE SUGGESTIONS SET SUGGESTION=:suggestion, LAST_UPDATE = CURRENT_TIMESTAMP() WHERE ID=:id";
			return handle.createUpdate(sql).bind("id", suggestion.getId())
					.bind("suggestion", suggestion.getSuggestion()).execute() > 0;
		});
	}
As you can see you still have to write SQL. But a lot of boiler plate code can be omitted.


This post is part of a series: