Getting into Thorntail v4

28 August 2018

What is Thorntail v4?

Thorntail v4 is in beta as of August 2018:

  • Thorntail v4 is an open-source java framework
  • Thorntail v4 is a java runtime for developing server-side applications
  • Thorntail v4 is like Java EE
  • Thorntail v4 is for building small, runnable jars from your projects
  • Thorntail v4 is modular and composable
  • Thorntail v4 is fast at starting and fast at serving requests
  • Thorntail v4 is a complete re-write of Thorntail v2 (which is currently released) that provides greater speed and flexibility

Why Thorntail v4?

If you want something event-driven, declarative and quick to learn for building scalable backends, Thorntail v4 is for you. It takes just a few minutes to build a server-side application with Thorntail v4 with a database, ReST API and anything else you can imagine.

Just a few of the great features you get with Thorntail v4 are:

  • flexible runtime config through environment variables and other means with the ability to specify default values (great for Kubernetes)
  • metrics gathering and reporting
  • ability of application instances to report their own health (can be customized if desired)
  • declarative ReST client
  • fault tolerance tools
  • testing tools
  • asynchronous events
  • support for SQL-based databases as well as others like Cassandra, CouchDB, Mongo and even Neo4j
  • web frontend

Getting Thorntail v4

Head over to my article on installing Thorntail v4 (here) to get it into your system’s maven cache

Using Thorntail v4

Unlike a typical JavaEE server, you shouldn’t think of the runtime as actually being separate from your program. In Thorntail, whatever you add as a maven dependency is part of the runtime, much like a Java SE project. Indeed, Thorntail v4 is not fully JavaEE and should not be treated as such in project setup.

Thorntail is built up of components designed for a particular task. The following are examples of the components I use most frequently:

Kernel

The very core of Thorntail transitively included in any Thorntail project. Comes with the following features:

  • CDI
  • CDI events (these classes can be found in the io.thorntail.events.LifecycleEvent class here)
  • Microprofile-Config

Jaxrs

Support for building declarative ReST APIs quickly and easily

JPA

Support for building applications using Hibernate with a database that has a JDBC driver such as MySQL, MariaDB, H2, etc.

OGM

Support for NoSQL databases with JPA like MongoDB, Apache Cassandra, CouchDB, Infinispan, Neo4j, etc. It’s a fantastic project I recommend reading more about here.

Sample Project

Thorntail v4 comes with a simple project template you can generate a project from with the following command:

$ mvn -B archetype:generate -DarchetypeGroupId=io.thorntail.archetypes -DarchetypeArtifactId=thorntail-jaxrs-archetype -DgroupId=com.example -DartifactId=sampleapp

Let’s break down the project maven just created for us:

$ tree sampleapp

sampleapp
├── pom.xml                                   # project descriptor
└── src
    ├── main                                  # application code
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           ├── MyApplication.java    # main method as well as Jax-RS metadata for the base url of the ReST API
    │   │           └── MyResource.java       # the actual ReST API code
    │   └── resources                         # non-code project files
    │       └── META-INF                      # project metadata
    │           ├── application.properties    # default configuration values for our application
    │           └── beans.xml                 # required file, can be empty, more on this later
    └── test                                  # test code
        └── java
            └── com
                └── example
                    └── MyTest.java           # test code for the ReST API

11 directories, 6 files

src/main/java/com/example/MyApplication.java

This file contains the Jax-RS Application class which configures the base url for the ReST API code. I’ll be doing an article on building ReST APIs with Thorntail v4 shortly.

package com.example;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

import io.thorntail.Thorntail;

@ApplicationPath("/") // sets the base url of the ReST API for this project to '/' because the entire project is a ReST API
public class MyApplication extends Application { // @ApplicationPath(String baseUrl) can only be used on classes which extend Application
    public static void main(String... args) throws Exception {
        Thorntail.run(); // runs Thorntail v4
    }
}

src/main/java/com/example/MyResource.java

This is a Jax-RS “endpoint” class. It’s @Path, or base url, is appended to the base url for the Jax-RS Application class, MyApplication in this project. It is a plain Java class and doesn’t need to be of any particular type. This class contains java methods corresponding to a specific url and/or specific HTTP method.

package com.example;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/") // sets the base url for this class relative to the base url of the ReST API project
public class MyResource {
    @GET // tells the router to invoke this method for HTTP GET requests
    @Produces(MediaType.TEXT_PLAIN) // sets the mime type of the data to be returned to 'text/plain'
    public String hello() { // Instead of managing the request router yourself, you just set the return type to whatever you want and the rest is automatic
        return "Hello World"; // No magic, just return a String and you're done
    }
}

src/main/resources/META-INF/application.properties

This file contains defaults for configuration data. If you wished to override the configuration at runtime, you could pass -Dlogging.level.io.undertow=$some_value at runtime.

#logging.level.io.undertow=ALL

src/main/resources/META-INF/beans.xml

This file is empty, but it’s there to enable dependency injection with CDI inside java classes. Just trust me that you don’t want to disable dependency injection. It is your friend.

src/test/java/com/example/MyTest.java

This file contains an integration test which checks that the application creates a ReST API which returns “Hello World” from the root of the web server.

package com.example;

import io.thorntail.test.ThorntailTestRunner;
import org.junit.Test;
import org.junit.runner.RunWith;

import static io.restassured.RestAssured.when;
import static org.hamcrest.Matchers.containsString;

@RunWith(ThorntailTestRunner.class) // Integration tests should be run with the ThorntailTestRunner class to be run in the container
public class MyTest {

    @Test
    public void test() {
        when().get("/").then() // When an HTTP GET is invoked on the '/' url
                .statusCode(200) // assert that the server returns a 200 OK as the response status
                .body(containsString("Hello World")); // assert the body contains the string "Hello World"
    }
}

This class does something very similar to Arquillian, but without the isolation between the app and the test and without requiring the definition of a deployment. It runs the tests with the whole app loaded so the test environment is as close to production as you can get.

Keep Going

Now that you’ve setup a basic project and learned how it works, you’re ready to explore Thorntail v4 in more detail. Check back on this blog for more.