Welcome to Quarkus, the lastest project for supersonic Java! ! Quarkus features a Cloud Native, Container first, Microservice ready framework for writing Java applications based on the standards and frameworks you are using today (Hibernate, RESTEasy, Camel, Vert.X etc).

What does it mean ?

  • Container First: Minimal footprint Java applications optimal for running in containers
  • Cloud Native: Embraces 12 factor architecture in environments like Kubernetes.
  • Microservice First: Brings lightning fast startup time and code turn around to Java apps

Here is a bird's eye view of the Project:

Quarkus io tutorial wildfly quarkus

As you can see, one of the most interesting aspect of quarkus is that you can build native executables using GraalVM. That makes Quarkus applications ideal for containers and serverless workloads. In this first tutorial we will learn how to build a Quarkus REST application using the Maven tooling available and add a minimal of compexity to it.

Building your first Quarkus application

Quarkus applications can be quickly bootstrapped using the Maven or Gradle plugin available. The plugin will generate a minimal project structure with a sample REST Endpoint and the Quarkus's Maven dependencies included in the configuration file. Let's assume you are using Maven:

mvn io.quarkus:quarkus-maven-plugin:0.11.0:create     -DprojectGroupId=com.sample     -DprojectArtifactId=hello-quarkus     -DclassName="com.sample.QuarkusEndpoint"     -Dpath="/hello"

The following project structure will be created:

└── hello-quarkus
    ├── pom.xml
    └── src
        ├── main
        │   ├── docker
        │   │   └── Dockerfile
        │   ├── java
        │   │   └── com
        │   │       └── sample
        │   │           └── QuarkusEndpoint.java
        │   └── resources
        │       └── META-INF
        │           ├── microprofile-config.properties
        │           └── resources
        │               └── index.html
        └── test
            └── java
                └── com
                    └── sample
                        ├── NativeQuarkusEndpointIT.java
                        └── QuarkusEndpointTest.java

The most interesting items are:

  • The QuarkusEndpoint.java containing the Hello REST Endpoint
  • A Dockerfile to build the application as a Container
  • An index.html file to remind you that you can add static files (HTML, images, Javascript) in that folder
  • Testing Junit classes under the folder "test"
  • A file named microprofile-config.properties for adding MicroProfile Configuration Features

Here is the quintessential REST Endpoint, which returns a text string upon invocation of the "hello()" GET Resource.

package com.sample;

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

@Path("/hello")
public class QuarkusEndpoint {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }
}

The pom.xml file, reflects the libraries needed to bootstrap the project. Here is the dependencies section of it:

  <properties>
    <surefire-plugin.version>2.22.0</surefire-plugin.version>
    <quarkus.version>0.11.0</quarkus.version>
    <maven.compiler.source>1.8</maven.compiler.source>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-bom</artifactId>
        <version>${quarkus.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-arc</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-junit5</artifactId>
    </dependency>
    <dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>rest-assured</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

You can build and run your project from the root folder with:

mvn clean compile quarkus:dev

The application will bootstrap Quarkus in a snap:

[INFO] Running com.sample.QuarkusEndpointTest
2019-03-14 16:46:19,743 INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
2019-03-14 16:46:20,292 INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 549ms
2019-03-14 16:46:20,610 INFO  [io.quarkus] (main) Quarkus 0.11.0 started in 0.294s. Listening on: http://127.0.0.1:8081

You can test it with:

curl http://localhost:8080/hello

That will return the "Hello" String

Debugging and Hot Deploying with Quarkus

As you can see, we have launched Quarkus with quarkus:dev which runs Quarkus in development mode. This enables hot deployment with background compilation, which means that when you modify your Java files your resource files and refresh your browser these changes will automatically take effect. This works too for resource files like the configuration property file. Refreshing the browser triggers a scan of the workspace, and if any changes are detected the Java files are recompiled and the application is redeployed; your request is then serviced by the redeployed application. If there are any issues with compilation or deployment an error page will let you know.

Also, in that mode, Quarkus will listen for a debugger on port 5005. If your want to wait for the debugger to attach before running you can pass -Ddebug on the command line. If you don’t want the debugger at all you can use -Ddebug=false.

Building a more complex application

We will now learn how to build a slightly more complex Quarkus application which still uses a REST Endpoint but has a minimal UI to insert data in memory.

You can re-use the same project you have just built, or create a new one from scratch. Start by adding a Java Bean to be used as Domain class:

package com.sample;

import java.util.Objects;

public class Person {

    String name;
    String surname;

    public Person( ) {  }

    public String getName() {
        return name;
    }
    public String getSurname() {  return surname;  }
    public void setName(String name) {
        this.name = name;
    }
    public void setSurname(String surname) {
        this.surname = surname;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", surname='" + surname + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name) &&
                Objects.equals(surname, person.surname);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, surname);
    }
}

Now a REST Endpoint to save this object in a Collection:

package com.sample;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Set;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/persons")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class RESTEndpoint {

    private Set<Person> persons = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>()));

    @GET
    public Set<Person> list() {
        return persons;
    }

    @POST
    public Set<Person> add(Person person) {
        System.out.println("Saving: " +person);
        persons.add(person);
        return persons;
    }

}

Basically, we have added a GET method to retrieve the list of Person, and a POST method to save a new Person in the Set.

As the above class can Produce and Consume JSON, we need to include also the quarkus-resteasy-jsonb artifact to our dependencies:

    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-jsonb</artifactId>
    </dependency>
    <dependency>

As it is, our application it is ready to be tested:

mvn clean compile quarkus:dev

You can add a new entry with cURL, passing as data the JSON to be consumed:

curl -d '{"name":"john", "surname":"black"}' -H "Content-Type: application/json" -X POST http://127.0.0.1:8080/persons

We can check that the data is available, with the corresponding GET request:

curl http://127.0.0.1:8080/persons
[{"name":"john","surname":"black"}]

Adding an UI to our Quarkus project

Finally, we can show how we can add Web application pages to our project. In order to do that, we need to add HTML pages under the following path:

│   └── resources
│       └── META-INF
│           └── resources

For example, we will add an HTML page using AngularJS to display the data in Tabular format, with a Form to add new items via POST:

<!doctype html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Quarkus REST service</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/wingcss/0.1.8/wing.min.css"/>
    <!-- Load AngularJS -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
    <script type="text/javascript">
      var app = angular.module("PersonManagement", []);

      //Controller Part
      app.controller("PersonManagementController", function ($scope, $http) {

        //Initialize page with empty data
        $scope.persons = [];

        $scope.form = {
          name: "",
          surname: ""
        };

        //Now load the data from server
        _refreshPageData();

        //HTTP POST methods for add data
        $scope.add = function () {
          var data = { "name": $scope.form.name, "surname": $scope.form.surname };

          $http({
            method: "POST",
            url: '/persons',
            data: angular.toJson(data),
            headers: {
              'Content-Type': 'application/json'
            }
          }).then(_success, _error);
        };


        //HTTP GET- get all persons collection
        function _refreshPageData() {
          $http({
            method: 'GET',
            url: '/persons'
          }).then(function successCallback(response) {
            $scope.persons = response.data;
          }, function errorCallback(response) {
            console.log(response.statusText);
          });
        }

        function _success(response) {
          _refreshPageData();
          _clearForm();
        }

        function _error(response) {
          alert(response.data.message || response.statusText);
        }

        //Clear the form
        function _clearForm() {
          $scope.form.name = "";
          $scope.form.surname = "";
        }
      });
    </script>
</head>
<body ng-app="PersonManagement" ng-controller="PersonManagementController">

<div class="container">
    <h1>Quarkus REST Service</h1>

    <form ng-submit="add()">
        <div class="row">
            <div class="col-6"><input type="text" placeholder="Name" ng-model="form.name" size="60"/></div>
        </div>
        <div class="row">
            <div class="col-6"><input type="text" placeholder="Surname" ng-model="form.surname" size="60"/></div>
        </div>
        <input type="submit" value="Save"/>
    </form>

    <h3>Person List</h3>
    <div class="row">
        <div class="col-4">Name</div>
        <div class="col-8">Surname</div>
    </div>
    <div class="row" ng-repeat="person in persons">
        <div class="col-4">{{ person.name }}</div>
        <div class="col-8">{{ person.surname }}</div>
    </div>
</div>

</body>
</html>

Again, run your application with:

mvn clean compile quarkus:dev

And see it in action:

Quarkus io tutorial wildfly quarkus

Now experiment changing anything in the HTML page, for example the Heading contained in it. As the application is running in Development mode, you will see your changes without restarting your app!

Quarkus io tutorial wildfly quarkus

Conclusion

In this first tutorial about Quarkus we have covered the basic set up of an application and the tooling which can be used to bootstrap your projects. In the next tutorial we will take a look at Quarkus Container Native stack to run a super-fast application in a Container! Stay tuned!

References: https://quarkus.io/

0
0
0
s2smodern