///////////////////////////////////////////////////////////////////////////////

    Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

///////////////////////////////////////////////////////////////////////////////

= Helidon MP Tutorial
:description: Helidon MP Tutorial
:keywords: helidon

This tutorial describes how to build a Helidon MicroProfile (MP) application from scratch
 including JSON REST endpoints, metrics, health check, and configuration.

== What you need

[width=50%,role="flex, sm7"]
|===
|About 30 minutes
|<<about/03_prerequisites.adoc,Helidon Prerequisites>>
|===

== Create The Maven Project

This tutorial demonstrates how to create the application from scratch, without
 using the Maven archetypes as a quickstart. 
 
Create a new empty directory for the project (for example, `helidon-mp-tutorial`).  Change into this directory.

Create a new Maven POM file (called `pom.xml`) and add the following
 content: 
 
[source,xml,subs="attributes+"]
.Initial Maven POM file
----
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent> <1>
        <groupId>io.helidon.applications</groupId>
        <artifactId>helidon-mp</artifactId>
        <version>{helidon.version}</version>
        <relativePath/>
    </parent>

    <groupId>io.helidon.examples</groupId>
    <artifactId>helidon-mp-tutorial</artifactId>  <2>
    <name>${project.artifactId}</name>

    <properties>
        <mainClass>io.helidon.examples.Main</mainClass> <3>
    </properties>

    <dependencies>
        <dependency>
            <groupId>io.helidon.microprofile.bundles</groupId>
            <artifactId>helidon-microprofile</artifactId> <4>
        </dependency>
    </dependencies>

    <build> <5>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-libs</id>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.jboss.jandex</groupId>
                <artifactId>jandex-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>make-index</id>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

----

The POM file contains the basic project information and configurations 
 needed to get started and does the following:  

<1> Includes the Helidon MP application parent pom. This parent pom
    contains dependency and plugin management to keep your application's
    pom simple and clean.
<2> Establishes the Maven coordinates for the new project.
<3> Sets the `mainClass` which will be used later
    when building a JAR file.  The class will be created later in this
    tutorial.
<4> Adds a dependency for the MicroProfile bundle which allows the
    use of MicroProfile features in the application. The helidon-mp
    parent pom includes dependency management, so you don't need to
    include a version number here. You will automatically use the
    version of Helidon that matches the version of the parent pom
    ({helidon.version} in this case).
<5> Adds plugins to be executed during the build. The `maven-dependency-plugin`
    is used to copy the runtime dependencies into your target directory. The
    `jandex-maven-plugin` builds an index of your class files for faster
    loading. The Helidon parent pom handles the details of configuring
    these plugins. But you can modify the configuration here.

TIP: MicroProfile contains features like Metrics, Health Check,
 Streams Operators, Open Tracing, OpenAPI, REST client, and fault
 tolerance. You can find detailed information about MicroProfile on the
 https://projects.eclipse.org/projects/technology.microprofile[Eclipse MicroProfile] site.

With this `pom.xml`, the application can be built successfully with Maven:

[source,bash]
----
mvn clean package
----

This will create a JAR file in the `target` directory.

TIP: The warning message `JAR will be empty - no content was marked for inclusion!` 
 can be ignored for now  because there is no actual content in the 
 application yet.

== Start implementing the MicroProfile application

The actual application logic can be created now.
 The first step is to add the application class. Create a directory
 for your source code, and then create directories for the package 
 hierarchy:

[source,bash]
.Create directories for source code
----
mkdir -p src/main/java/io/helidon/examples
----

The application will be a simple REST service that will return a 
 greeting to the caller.  The first iteration of the application will 
 contain a MicroProfile application class, a resource class and a 
 Main class which will be used to start up the Helidon server 
 and the application.

TIP: Technically, the main class is not needed unless you want to control
 the startup sequence.  You can set the `mainClass` to 
 `io.helidon.microprofile.Main` and it will find the application
 annotated with `ApplicationScoped`.  Though it is likely that
 almost any "real" application would want its own main class.

Create a new Java class inside that directory called `GreetApplication.java`
 and add the following content:

[source,java]
.src/main/java/io/helidon/examples/GreetApplication.java
----
package io.helidon.examples;

import io.helidon.common.CollectionsHelper;

import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.Set;

@ApplicationScoped <1>
@ApplicationPath("/") <2>
public class GreetApplication extends Application { <3>

    @Override 
    public Set<Class<?>> getClasses() { <4>
        return CollectionsHelper.setOf(GreetResource.class);
    }

}
----


<1> The class is annotated with `ApplicationScoped` 
 which specifies that this bean is application scoped. The application scope
 is shared between all web service invocations that execute within the same
 application. 
<2> The `ApplicationPath` annotation sets the URL path that will be used to 
 access the application.
<3> The application class must extend `javax.ws.rs.core.Application`. 
<4> In the application class, override the 
 `public Set<Class<?>> getClasses()` method.  This method must return
 a `Set` of `Class` objects, one for each resource that the application 
 will serve.  Currently, the application only has one resource, the
 `GreetResource`.

TIP:  You can learn more about scopes and contexts, and how they are used 
 from the https://docs.jboss.org/cdi/api/2.0/index.html[Specification].

The `GreetResource` is defined in the `GreetResource.java` class as shown
below: 

[source,java]
.src/main/java/io/helidon/examples/GreetResource.java
----
package io.helidon.examples;

import javax.enterprise.context.RequestScoped;
import javax.json.Json;
import javax.json.JsonBuilderFactory;
import javax.json.JsonObject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.Collections;

@Path("/greet") <1>
@RequestScoped <2>
public class GreetResource {

    private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public JsonObject getDefaultMessage() { <3>
        return JSON.createObjectBuilder()
                .add("message", "Hello World")
                .build(); <4>
    }

}
----

<1> This class is annotated with `Path` which sets the path for this resource
 as `/greet`.  
<2> The `RequestScoped` annotation defines that this bean is
 request scoped.  The request scope is active only for the duration of
 one web service invocation and it is destroyed at the end of that
 invocation.
<3> A `public JsonObject getDefaultMessage()` method is defined
 which is annotated with `GET`, meaning it will accept the HTTP GET method.
 It is also annotated with `Produces(MediaType.APPLICATION_JSON)` which
 declares that this method will return JSON data. 
<4> The method body creates
 a JSON object containing a single object named "message" with the content
 "Hello World".  This method will be expanded and improved
 later in the tutorial.

TIP: So far this is just a JAX-RS application, with no Helidon or MicroProfile
 specific code in it.  There are many JAX-RS tutorials available if you 
 want to learn more about this kind of application.

A main class is also required to start up the server and run the
 application.  Here is the initial main class:

[source,java]
.src/main/java/io/helidon/examples/Main.java
----
package io.helidon.examples;

import io.helidon.microprofile.server.Server;
import java.io.IOException;

public final class Main {

    private Main() { } <1>

    public static void main(final String[] args) throws IOException {
        Server server = startServer();
        System.out.println("http://localhost:" + server.port() + "/greet");
    }

    static Server startServer() {
        return Server.create().start(); <2>
    }
    
}
----

In this class, a `main` method is defined which starts the Helidon MP 
 server and prints out a message with the listen address. 

<1> Notice that
 this class has an empty no-args constructor to make sure this class
 cannot be instantiated.
<2> The MicroProfile server is started with the default configuration.

Helidon MP applications also require a `beans.xml` resource file to
 tell Helidon to use the annotations discussed above to discover Java
 beans in the application.

Create a `beans.xml` in the `src/main/resources/META-INF` directory
 with the following content:

[source,xml]
.src/main/resources/META-INF/beans.xml
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                           http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
       version="2.0"
       bean-discovery-mode="annotated"> <1>
</beans>
----

<1> The `bean-discovery-mode` tells Helidon to look for the annotations
 to discover Java beans in the application.

== Build the application

Helidon MP applications are packaged into a JAR file and the dependencies
 are copied into a `libs` directory.  A detailed explanation of 
 why this particular packaging approach is recommended is provided on the 
 <<guides/30_dockerfile.adoc,Creating Docker Images>> page.

 You can now build the application.

[source,bash]
.Build the Application
----
mvn package
----

This will build the application jar and save all runtime 
dependencies in the `target/libs` directory. This means you can easily start the
 application by running the application jar file:

[source,bash]
.Run the application
----
java -jar target/helidon-mp-tutorial.jar
----

At this stage, the application is a very simple "Hello World" greeting service. 
 It supports a single GET request for generating a greeting message.
 The response is encoded using JSON.
 For example:

[source,bash]
.Try the Application
----
curl -X GET http://localhost:7001/greet
{"message":"Hello World!"}
----

In the output you can see the JSON output from the `getDefaultMessage()`
 method that was discussed earlier.  The server has used a default port
 `7001`.  The application can be stopped cleanly by pressing Ctrl+C.

== Configuration

Helidon MP applications can use the `META-INF/microprofile-config.properties` 
 file to specify configuration data.  This file (resource) is read by default 
 if it is present on the classpath. Create this file in  
 `src/main/resources/META-INF` with the following content:

[source,bash] 
.Initial microprofile-config.properties
----
# Microprofile server properties
server.port=8080
server.host=0.0.0.0
----

Rebuild the application and run it again.  Notice that it now uses port
 8080 as specified in the configuration file.

TIP: You can learn more about options for configuring the Helidon Server on the
 <<microprofile/02_server-configuration.adoc,Configuring the Server>> page.

In addition to predefined server properties, application-specific 
 configuration information can be added to this file.  Add the `app.greeting`
 property to the file as shown below. This property will be used to set the
 content of greeting message.

[source,bash] 
.Updated META-INF/microprofile-config.properties
----
# Microprofile server properties
server.port=8080
server.host=0.0.0.0

# Application properties
app.greeting=Hello
----

Add a new "provider" class to read this property and make it available
 to the application.  The class will be called `GreetingProvider.java`
 and have the following content:

[source,java]
.src/main/java/io/helidon/examples/GreetingProvider.java
----
package io.helidon.examples;

import org.eclipse.microprofile.config.inject.ConfigProperty;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.util.concurrent.atomic.AtomicReference;

@ApplicationScoped <1>
public class GreetingProvider {
  private final AtomicReference<String> message = new AtomicReference<>(); <2>

  @Inject <3>
  public GreetingProvider(@ConfigProperty(name = "app.greeting") String message) {
    this.message.set(message);
  }

  String getMessage() {
    return message.get();
  }

  void setMessage(String message) {
    this.message.set(message);
  }
}
----

<1> This class also has the `ApplicationScoped` annotation, so it will persist
 for the life of the application.  
<2> The class contains an `AtomicReference` 
 to a `String` where the greeting will be stored.  The `AtomicReference` 
 provides lock-free thread-safe access to the underlying `String`.
<3> The `public GreetingProvider(...)` constructor is annotated with `Inject`
 which tells Helidon to use Contexts and Dependency Injection to provide
 the needed values.  In this case, the `String message` is annotated with
 `ConfigProperty(name = "app.greeting")` so Helidon will inject the 
 property from the configuration file with the key `app.greeting`.
 This method demonstrates how to read configuration information into
 the application.  A getter and setter are also included in this class.

The `GreetResource` must be updated to use this value instead of the 
 hard coded response.  Make the following updates to that class:

[source,java]
.Updated GreetResource class
----
package io.helidon.examples;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.json.Json;
import javax.json.JsonBuilderFactory;
import javax.json.JsonObject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.Collections;

@Path("/greet")
@RequestScoped
public class GreetResource {

    private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());
    private final GreetingProvider greetingProvider;

    @Inject <1>
    public GreetResource(GreetingProvider greetingConfig) {
        this.greetingProvider = greetingConfig;
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public JsonObject getDefaultMessage() {
        return createResponse("World"); <2>
    }

    private JsonObject createResponse(String who) { <3>
        String msg = String.format("%s %s!", greetingProvider.getMessage(), who);

        return JSON.createObjectBuilder()
                .add("message", msg)
                .build();
    }

}
---- 

<1> This updated class adds a `GreetingProvider` and uses constructor injection
 to get the value from the configuration file.  
<2> The logic to create the 
 response message is refactored into a `createResponse` method and the 
 `getDefaultMessage()` method is updated to use this new method.
<3> In `createResponse()` the message is obtained from the `GreetingProvider`
 which in turn got it from the configuration files. 

Rebuild and run the application. Notice that it now uses the greeting
 from the configuration file.  Change the configuration file and restart
 the application, notice that it uses the changed value.

////
==== Dynamic configuration

Helidon also allows applications to consume configuration from multiple
 sources and to consume changes to the configuration dynamically, without requiring a restart. Update the main class as follows:

[source,java] 
.Updated main class
----
package io.helidon.examples;

import io.helidon.config.Config;
import io.helidon.config.PollingStrategies;
import io.helidon.microprofile.server.Server;

import java.io.IOException;

import static io.helidon.config.ConfigSources.classpath;
import static io.helidon.config.ConfigSources.file;

public final class Main {

    private Main() { }

    public static void main(final String[] args) throws IOException {
        Server server = startServer();
        System.out.println("http://localhost:" + server.port() + "/greet");
    }

    private static Config buildConfig() {
        return Config.builder()
                .sources(
                        file("conf/mp.yaml") <1>
                            .pollingStrategy(PollingStrategies::watch)
                            .optional(),
                        classpath("META-INF/microprofile-config.properties"))
                .build();
    }

    static Server startServer() {
        return Server.builder()
                     .config(buildConfig()) <2>
                     .build()
                     .start();
    }

}
----

These updates introduce a new configuration file `conf/mp.yaml` which 
 can be kept outside of the JAR file, and which can be used to provide
 additional configuration data and/or to override the data in the
 `microprofile-config.properties` which will be packaged in the JAR file.

Layered configuration like this, where the configuration is read from 
 more than one source with overrides, is commonly used in cases where 
 you want to have different configurations for different environments
 like development, test and production.

TIP: Learn much more about configuration in <<config/06_advanced-configuration.adoc,Advanced Configurtion Topics>>.

Create this file with the following content: 

[source,yaml]
.Initial mp.yaml configuration file
----
app:
  greeting: "Hallo"
----

<1> The new `buildConfig()` method defines two sources for configuration information, first the
 new `mp.yaml` file, and this is marked as optional, and has a "polling
 strategy" of "watch".  The polling strategy tells Helidon to watch for 
 any updates to this file and to dynamically update the configuration.
 The second entry is the `microprofile-config.properties` file from the
 classpath.
<2> Notice that the `startServer()` method has been updated to use a
 Config Builder, which is implemented in the new method `buildConfig()`.

Rebuild the application and run it.  After making some requests, update
 the greeting in the `mp.yaml` and observe that new requests pick up
 the new value without the need for a restart of the application.

////

TIP: To learn more about Helidon MP configuration please see the 
 <<config/01_introduction.adoc,Config>> section of the documentation.

== Extending the application

In this section, the application will be extended to add a PUT 
 resource method which will allow users to update the greeting and a
 second GET resource method which will accept a parameter.

Here are the two new methods to add to `GreetResource.java`:

[source,java]
.New methods for GreetResource.java
----
import javax.ws.rs.Consumes;
import javax.ws.rs.PUT;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;

    // some lines omitted

    @Path("/{name}")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public JsonObject getMessage(@PathParam("name") String name) { <1>
        return createResponse(name);
    }
    
    @Path("/greeting")
    @PUT
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response updateGreeting(JsonObject jsonObject) { <2>

        if (!jsonObject.containsKey("greeting")) {
            JsonObject entity = JSON.createObjectBuilder()
                    .add("error", "No greeting provided")
                    .build();
            return Response.status(Response.Status.BAD_REQUEST).entity(entity).build();
        }

        String newGreeting = jsonObject.getString("greeting");

        greetingProvider.setMessage(newGreeting);
        return Response.status(Response.Status.NO_CONTENT).build();
    }
----

<1> The first of these two methods implements a new HTTP GET service 
 that returns JSON and it has a path parameter.  The `Path` annotation
 defines the next part of the path to be a parameter named `name`.
 In the method arguments the `PathParam("name")` annotation on 
 `String name` has the effect of passing the parameter from the 
 URL into this method as `name`.
<2> The second method implements a new HTTP PUT service which produces
 and consumes JSON, note the `Consumes` and `PUT` annotations.
 It also defines a path of "/greeting".  Notice that the method
 argument is a `JsonObject`. Inside the method body there is code
 to check for the expected JSON, extract the value and update the
 message in the `GreetingProvider`.

Rebuild and run the application.  Test the new services using curl
 commands similar to those shown below:

[source,bash]
.Testing the new services
----
curl -X GET http://localhost:8080/greet
{"message":"Hello World!"}

curl -X GET http://localhost:8080/greet/Joe
{"message":"Hello Joe!"}

curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Hola"}' http://localhost:8080/greet/greeting

curl -X GET http://localhost:8080/greet/Jose
{"message":"Hola Jose!"}
----

Helidon MP provides many other features which can be added to the application.

== Logging

The application logging can be customized.  The default logging provider
 is `java.util.logging`, however it is possible to use other providers.
 In this tutorial the default provider is used.

Create a `logging.properties` file in `src/main/resources` with
 the following content:

[source,java]
.Example logging.properties file
----
# Send messages to the console
handlers=io.helidon.common.HelidonConsoleHandler

# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread
java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n

# Global logging level. Can be overridden by specific loggers
.level=INFO
----

<1> The Helidon console logging handler is configured. This handler writes to `System.out`, does not filter by level
and uses a custom `SimpleFormatter` that supports thread names.
<2> The format string is set using the standard options to include the timestamp,
 thread name and message.
<3> The global logging level is set to `INFO`.

Update the main class to configure logging as shown below:

[source,java]
.Updated main class with logging 
----
import java.util.logging.LogManager;

    // some lines omitted

    public static void main(final String[] args) throws IOException {
        setupLogging(); <1>
        Server server = startServer();
        System.out.println("http://localhost:" + server.port() + "/greet");
    }

    private static void setupLogging() throws IOException { <2>
        LogManager.getLogManager().readConfiguration(
                Main.class.getResourceAsStream("/logging.properties"));
    }
----

<1> In the `main()` method introduce a call to a new `setupLogging()` method.
<2> This new method reads the logging configuration from the `logging.properties`
 file and updates the `LogManager` to use that configuration.

Rebuild and run the application and notice the new logging format takes effect.

[source,bash]
.Log output
----
// before
Aug 22, 2019 11:10:11 AM io.helidon.webserver.NettyWebServer lambda$start$8
INFO: Channel '@default' started: [id: 0xd0afba31, L:/0:0:0:0:0:0:0:0:8080]
Aug 22, 2019 11:10:11 AM io.helidon.microprofile.server.ServerImpl lambda$start$10
INFO: Server started on http://localhost:8080 (and all other host addresses) in 182 milliseconds.
http://localhost:8080/greet

// after
2019.08.22 11:24:42 INFO io.helidon.webserver.NettyWebServer Thread[main,5,main]: Version: 1.2.0
2019.08.22 11:24:42 INFO io.helidon.webserver.NettyWebServer Thread[nioEventLoopGroup-2-1,10,main]: Channel '@default' started: [id: 0x8f652dfe, L:/0:0:0:0:0:0:0:0:8080]
2019.08.22 11:24:42 INFO io.helidon.microprofile.server.ServerImpl Thread[nioEventLoopGroup-2-1,10,main]: Server started on http://localhost:8080 (and all other host addresses) in 237 milliseconds.
http://localhost:8080/greet
----

== Metrics

Helidon provides built-in support for metrics endpoints.

[source,bash]
.Metrics in Prometheus Format
----
curl -s -X GET http://localhost:8080/metrics
----

[source,bash]
.Metrics in JSON Format
----
curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics
----


It is possible to disable metrics by adding properties to the 
`microprofile-config.properties` file, for example:

[source,bash]
.Disable a metric
----
metrics.base.classloader.currentLoadedClass.count.enabled=false
----

Call the metrics endpoint before adding this change to confirm that the metric
is included, then add the property to disable the metric, rebuild and restart
the application and check again:

[source,bash]
.Checking metrics before and after disabling the metric
----
# before
curl -s http://localhost:8080/metrics | grep classloader_current
# TYPE base:classloader_current_loaded_class_count counter
# HELP base:classloader_current_loaded_class_count Displays the number of classes that are currently loaded in the Java virtual machine.
base:classloader_current_loaded_class_count 7936

# after
curl -s http://localhost:8080/metrics | grep classloader_current
# (no output)
----

Helidon also support custom metrics.  To add a new metric, annotate the 
 JAX-RS resource with one of the metric annotations as shown in the example
 below:

TIP:  You can find details of the available annotations in the 
 https://microprofile.io/project/eclipse/microprofile-metrics[MicroProfile Metrics
 Specification].

[source,java]
.Updated GreetResource.java with custom metrics
----
import org.eclipse.microprofile.metrics.annotation.Timed;

    // some lines omitted

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Timed <1>
    public JsonObject getDefaultMessage() {
        return createResponse("World");
    }
----

<1> The `Timed` annotation is added to the `getDefaultMessage()` method.

Rebuild and run the application.  Make some calls to the endpoint 
 (http://localhost:8080/greet) so there will be some data to report.
 Then obtain the application metrics as follows:

[source,bash]
.Checking the application metrics
----
curl -H "Accept: application/json" http://localhost:8080/metrics/application
{
  "io.helidon.examples.GreetResource.getDefaultMessage": {
    "count": 2,
    "meanRate": 0.036565171873527716,
    "oneMinRate": 0.015991117074135343,
    "fiveMinRate": 0.0033057092356765017,
    "fifteenMinRate": 0.0011080303990206543,
    "min": 78658,
    "max": 1614077,
    "mean": 811843.8728029992,
    "stddev": 766932.8494434259,
    "p50": 78658,
    "p75": 1614077,
    "p95": 1614077,
    "p98": 1614077,
    "p99": 1614077,
    "p999": 1614077
  }
}
----

Learn more about using Helidon and MicroProfile metrics in the <<guides/09_metrics_mp_guide.adoc,Metrics Guide>>.


== Health Check

Helidon provides built-in support for health check endpoints.  Obtain 
 the built-in health check using the following URL: 

[source,bash]
.Health check
----
curl -s -X GET http://localhost:8080/health
{
  "outcome": "UP",
  "status": "UP",
  "checks": [
    {
      "name": "deadlock",
      "state": "UP",
      "status": "UP"
    },
    {
      "name": "diskSpace",
      "state": "UP",
      "status": "UP",
      "data": {
        "free": "381.23 GB",
        "freeBytes": 409340088320,
        "percentFree": "43.39%",
        "total": "878.70 GB",
        "totalBytes": 943491723264
      }
    },
    {
      "name": "heapMemory",
      "state": "UP",
      "status": "UP",
      "data": {
        "free": "324.90 MB",
        "freeBytes": 340682920,
        "max": "3.46 GB",
        "maxBytes": 3715629056,
        "percentFree": "97.65%",
        "total": "408.00 MB",
        "totalBytes": 427819008
      }
    }
  ]
}
----

Endpoints for readiness and liveness checks are also provided by default.
 Obtain the default results using these URLs, which return the same result as the previous example.:

[source,bash]
.Default readiness and liveness endpoints
----
# readiness
curl -i  -X GET http://localhost:8080/health/ready

# liveness
curl -i  -X GET http://localhost:8080/health/live
----

Helidon allows the addition of custom health checks to applications.
 Create a new class `GreetHealthcheck.java` with the following content:

[source,java]
.src/main/java/io/helidon/examples/GreetHealthcheck.java
----
package io.helidon.examples;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;

@Liveness <1>
@ApplicationScoped <2> 
public class GreetHealthcheck implements HealthCheck {
  private GreetingProvider provider;

  @Inject <3>
  public GreetHealthcheck(GreetingProvider provider) {
    this.provider = provider;
  }

  @Override
  public HealthCheckResponse call() { <4>
    String message = provider.getMessage();
    return HealthCheckResponse.named("greeting") <5>
        .state("Hello".equals(message))
        .withData("greeting", message)
        .build();
  }
}
----

<1> This class has the MicroProfile `Liveness` annotation which tells
 Helidon that this class provides a custom health check.  You can 
 learn more about the available annotations in the https://github.com/eclipse/microprofile-health/blob/master/spec/src/main/asciidoc/protocol-wireformat.adoc[MicroProfile Health Protocol and Wireformat] document.
<2> This class also has the `ApplicationScoped` annotation, as seen previously.
<3> The `GreetingProvider` is injected using Context and Dependency
 Injection.  This example will use the greeting to determine whether
 the application is healthy, this is a contrived example for demonstration
 purposes.
<4> Health checks must implement the `HealthCheck` functional interface, which
 includes the method `HealthCheckResponse call()`.  Helidon will invoke the
 `call()` method to verify the healthiness of the application.
<5> In this example, the application is deemed to be healthy if the 
 `GreetingProvider,getMessage()` method returns the string `"Hello"`
 and unhealthy otherwise.

Rebuild the application, make sure that the `mp.conf` has the `greeting` set
 to something other than `"Hello"` and then run the application and check 
 the health:

[source,bash]
.Custom health check reporting unhealthy state
----
curl -i -X GET http://localhost:8080/health/live 
HTTP/1.1 503 Service Unavailable <1>
Content-Type: application/json
Date: Fri, 23 Aug 2019 10:07:23 -0400
transfer-encoding: chunked
connection: keep-alive

{"outcome":"DOWN","status":"DOWN","checks":[{"name":"deadlock","state":"UP","status":"UP"},{"name":"diskSpace","state":"UP","status":"UP","data":{"free":"381.08 GB","freeBytes":409182306304,"percentFree":"43.37%","total":"878.70 GB","totalBytes":943491723264}},{"name":"greeting","state":"DOWN","status":"DOWN","data":{"greeting":"Hey"}},{"name":"heapMemory","state":"UP","status":"UP","data":{"free":"243.81 MB","freeBytes":255651048,"max":"3.46 GB","maxBytes":3715629056,"percentFree":"98.58%","total":"294.00 MB","totalBytes":308281344}}]} <2>
----

<1> The HTTP return code is now 503 Service Unavailable.
<2> The status is reported as "DOWN" and the custom check is included in 
 the output.

Now update the greeting to `"Hello"` using the following request, and then 
 check health again:

[source,bash]
.Update the greeting and check health again
----
# update greeting
curl -i -X PUT -H "Content-Type: application/json" -d '{"greeting": "Hello"}' http://localhost:8080/greet/greeting
HTTP/1.1 204 No Content <1>
Date: Thu, 22 Aug 2019 13:29:57 -0400
connection: keep-alive

# check health
curl -i -X GET http://localhost:8080/health/live 
HTTP/1.1 200 OK <2>
Content-Type: application/json
Date: Fri, 23 Aug 2019 10:08:09 -0400
connection: keep-alive
content-length: 536

{"outcome":"UP","status":"UP","checks":[{"name":"deadlock","state":"UP","status":"UP"},{"name":"diskSpace","state":"UP","status":"UP","data":{"free":"381.08 GB","freeBytes":409179811840,"percentFree":"43.37%","total":"878.70 GB","totalBytes":943491723264}},{"name":"greeting","state":"UP","status":"UP","data":{"greeting":"Hello"}},{"name":"heapMemory","state":"UP","status":"UP","data":{"free":"237.25 MB","freeBytes":248769720,"max":"3.46 GB","maxBytes":3715629056,"percentFree":"98.40%","total":"294.00 MB","totalBytes":308281344}}]} <3>
----

<1> The PUT returns a HTTP 204.
<2> The health check now returns a HTTP 200.
<3> The status is now reported as "UP" and the details are provided in the
 checks.

Learn more about health checks in the <<guides/07_health_mp_guide.adoc,Health Check Guide>>.

== Build a Docker Image

To run the application in Docker (or Kubernetes), a `Dockerfile` is needed
 to build a Docker image. To build the Docker image, you need to have Docker installed and running on your system.

Add a new `Dockerfile` in the project root directory with the following content:

[source,bash]
.Dockerfile content
----
FROM maven:3.5.4-jdk-9 as build <1>
WORKDIR /helidon

ADD pom.xml .
RUN mvn package -DskipTests <2>

ADD src src
RUN mvn package -DskipTests <3> 
RUN echo "done!"

FROM openjdk:8-jre-slim <4>
WORKDIR /helidon

COPY --from=build /helidon/target/helidon-mp-tutorial-1.0-SNAPSHOT.jar ./ <5>
COPY --from=build /helidon/target/libs ./libs

CMD ["java", "-jar", "helidon-mp-tutorial-1.0-SNAPSHOT.jar"] <6>
EXPOSE 8080
----

<1> This Dockerfile uses Docker's multi-stage build feature.  The `FROM`
 keyword creates the first stage.  In this stage, the base container has
 the build tools needed to build the application.  These are not required
 to run the application, so the second stage uses a smaller container.
<2> Add the `pom.xml` and running an "empty" maven build will download
 all of the dependencies and plugins in this layer.  This will make future
 builds faster because they will use this cached layer rather than downloading
 everything again.
<3> Add the source code and do the real build.
<4> Start a second stage using a much smaller runtime image.
<5> Copy the binary and libraries from the first stage.
<6> Set the initial command and expose port 8080.

To create the Docker image, use the following command: 

[source,bash]
.Docker build
----
docker build -t helidon-mp-tutorial .
----

Make sure the application is shutdown if it was still running
 locally so that port 8080 will not be in use, then start the application
 in Docker using the following command:

[source,bash]
.Run Docker Image
----
docker run --rm -p 8080:8080 helidon-mp-tutorial:latest
----

Try the application as before.

[source,bash]
.Try the application
----
curl http://localhost:8080/greet/bob
{"message":"Howdee bob!"}

curl http://localhost:8080/health/ready
{"outcome":"UP","status":"UP","checks":[]}
----

Learn more about creating Docker images for Helidon applications at
 <<guides/30_dockerfile.adoc,Creating Docker Images>>.

== Deploy the application to Kubernetes

If you don't have access to a Kubernetes cluster, you can
<<about/05_kubernetes.adoc,install one on your desktop>>.
Then deploy the example:

[source,bash]
.Verify connectivity to cluster
----
kubectl cluster-info
kubectl get nodes
----

To deploy the application to Kubernetes, a Kubernetes YAML file that 
 defines the deployment and associated resources is needed.  In this 
 case all that is required is the deployment and a service. 

Create a file called `app.yaml` in the project's root directory with 
 the following content: 

[source,yaml]
.Kubernetes YAML file
----
---
kind: Service <1>
apiVersion: v1
metadata:
  name: helidon-mp-tutorial
  labels:
    app: helidon-mp-tutorial
spec:
  type: NodePort <2>
  selector:
    app: helidon-mp-tutorial
  ports:
    - port: 8080
      targetPort: 8080
      name: http
---
kind: Deployment <3>
apiVersion: extensions/v1beta1
metadata:
  name: helidon-mp-tutorial
spec:
  replicas: 1 <4>
  template:
    metadata:
      labels:
        app: helidon-mp-tutorial
        version: v1
    spec:
      containers:
        - name: helidon-mp-tutorial
          image: helidon-mp-tutorial <5>
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
----

<1> Define a Service to provide access to the application. 
<2> Define a NodePort to expose the application outside the Kubernetes 
 cluster.
<3> Define a Deployment of the application.
<4> Define how many replicas of the application to run.
<5> Define the Docker image to use - this must be the one that was built
 in the previous step.  If the image was built on a different machine to the
 one where Kubernetes is running, or if Kubernetes is running on multiple 
 machines (worker nodes) then the image must either be manually copied to
 each node or otherwise pushed to a Docker registry that is accessible to
 the worker nodes.

This Kubernetes YAML file can be used to deploy the application to Kubernetes:

[source,bash]
.Deploy the application to Kubernetes
----
kubectl create -f app.yaml
kubectl get pods                    # Wait for quickstart pod to be RUNNING
----

TIP: Remember, if Kubernetes is running on a different machine, or inside 
 a VM (as in Docker for Desktop) then the Docker image must either be manually
 copied to the Kubernetes worker nodes or pushed to a Docker registry that 
 is accessible to those worker nodes.  Update the `image` entry in the 
 example above to include the Docker registry name.  If the registry is
 private a Docker registry secret will also be required.

The step above created a service that is exposed using any available node 
 port. Kubernetes allocates a free port. Lookup the service to find the port.

[source,bash]
.Lookup the service
----
kubectl get service helidon-mp-tutorial
----

Note the PORTs. The application can be exercised as before but use
 the second port number (the NodePort) instead of 8080. For example:

[source,bash]
.Access the application
----
curl -X GET http://localhost:31431/greet
----

If desired, the Kubernetes YAML file can also be used to remove the 
 application from Kubernetes as follows:

[source,bash]
.Remove the application from Kubernetes
----
kubectl delete -f app.yaml
----

== Conclusion

This concludes the tutorial.  The tutorial has demonstrated how to build
 a Helidon MP application from scratch, how to use Helidon and MicroProfile
 configuration, logging, metrics, and health checks.  It also demonstrated
 how to package the application in a Docker image and run it in Kubernetes.

There were several links to more detailed information included in the
 tutorial.  These links are repeated below and can be explored to learn 
 more details about Helidon application development.

==== Related links

* https://projects.eclipse.org/projects/technology.microprofile[Eclipse MicroProfile]
* https://docs.jboss.org/cdi/api/2.0/index.html[Contexts and Dependency Injection Specification]
* <<guides/30_dockerfile.adoc,Creating Docker Images>>
* <<microprofile/02_server-configuration.adoc,Configuring the Server>>
* <<config/01_introduction.adoc,Config>>
* https://microprofile.io/project/eclipse/microprofile-metrics[MicroProfile Metrics Specification]
* <<guides/09_metrics_mp_guide.adoc,Metrics Guide>>
* https://github.com/eclipse/microprofile-health/blob/master/spec/src/main/asciidoc/protocol-wireformat.adoc[MicroProfile Health Protocol and Wireformat]
* <<about/05_kubernetes.adoc,Install Kubernetes on your desktop>>
