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

    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.

///////////////////////////////////////////////////////////////////////////////
:java-base: src/main/java/io/helidon/guides/se/restfulwebservice
:greet-service: {java-base}/GreetService.java
:main-class: {java-base}/Main.java
:pom: pom.xml
:src-main-resources: src/main/resources
:base-example-getting-started: ../../../docs/src/main/docs/getting-started/02_base-example.adoc
:artifact-id: se-restful-webservice


= The RESTful Web Service Guide
:description: Helidon guide restful web service
:keywords: helidon, guide, example
:toc: preamble
:toclevels: 2

Create and build a RESTful web service as your first Helidon SE application.

== What you will learn
You'll learn how to use Helidon quickly to create a RESTful web service that accepts these HTTP requests:

[width=75%]
|===
|Method and URL | Result

|`GET localhost:8080/greet` |Returns a generic but friendly greeting
|`GET localhost:8080/greet/Joe` |Returns a personalized greeting for the specified person
|`PUT -H "Content-Type: application/json" -d '{"greeting" : "Hola"}'
localhost:8080/greet/greeting` |Changes the greeting used in subsequent responses
|===

You'll create the app in three main steps:

. Use the Helidon Maven archetype to create a basic Helidon SE app that responds
to the HTTP requests.

. Add code to perform a simple app-specific health check.

. Add code to record a simple app-specific metric.

As you develop the app, this guide helps you understand what each part of the
code does. If you prefer to download the finished code for this example, follow the
instructions at <<downloading,Download the example source>>.

== What you need

[width=50%]
|===
|About 15 minutes
|An IDE or text editor
|JDK 1.8 or later
|Maven 3.5 or later
|===

//Optional:
//|===
//|Docker 18.02 (use the Edge channel to run Kubernetes on your desktop)
//|`kubectl` 1.7.4
//|===

== Develop your application

=== Generate the Maven project using the Helidon archetype
Helidon provides a Maven archetype you can use to create a new Helidon project that
includes sample code.

1. `cd` to a directory that is not already a Maven project.
2. Run this command:
+
--
[source,bash,subs="attributes+"]
.Creating a new Helidon SE project
----
mvn archetype:generate -DinteractiveMode=false \
    -DarchetypeGroupId=io.helidon.archetypes \
    -DarchetypeArtifactId=helidon-quickstart-se \
    -DarchetypeVersion={helidon-version} \
    -DgroupId=io.helidon.guides \
    -DartifactId={artifact-id} \
    -Dpackage=io.helidon.guides.se.restfulwebservice
----

Running the archetype this way creates a subdirectory `{artifact-id}`
(using the `artifactId` setting from the archetype invocation) that contains a new
Maven project for a Helidon service.
--

=== Browse the generated source

==== `pom.xml`
The input you provided to the archetype determines the project's Maven
coordinates:
[source,xml,indent=0]
// _include::0-0:{pom}[tag=coordinates]
----
    <artifactId>se-restful-webservice</artifactId>
----

In the <dependency-management> section, note the dependency on the `helidon-bom`
POM:
[source,xml,subs="verbatim,attributes"]
// _include::0-6:{pom}[tag=bom,indent=0]
----
<dependency>
    <groupId>io.helidon</groupId>
    <artifactId>helidon-bom</artifactId>
    <version>${helidon.version}</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
----

Later, in the `<dependencies>` section, you will see declarations for various
Helidon components:

* web server
* config support for YAML
* health check
* metrics

The `helidon-bom` dependency adds `<dependency-management>` declarations for all the Helidon components.
You can add Helidon dependencies to your project without having to specify the
version. For example, see the declaration for config YAML support:
[source,xml]
// _include::0-3:{pom}[tag=configYamlDependency,indent=0]
----
<dependency>
    <groupId>io.helidon.config</groupId>
    <artifactId>helidon-config-yaml</artifactId>
</dependency>
----

==== `src/main/resources/application.yaml`: a config resource file for the application
Your app uses Helidon config to initialize the greeting and set up the HTTP
listener.

.`src/main/resources/application.yaml`
[source,yaml]
// _include::0-5:{src-main-resources}/application.yaml[tag=configContent]
----
app:
  greeting: "Hello" # <1>

server:             # <2>
  port: 8080
  host: 0.0.0.0
----
<1> Sets the initial greeting text for responses from the service
<2> Sets how the service will listen for requests

==== `src/main/resources/logging.properties`
This file controls logging.
.`src/main/resources/logging.properties`
[source]
// _include::0-10:{src-main-resources}/logging.properties[tag=loggingProps]
----
# Send messages to the console
handlers=java.util.logging.ConsoleHandler

# Global default logging level. Can be overriden by specific handlers and loggers
.level=INFO

# Helidon Web Server has a custom log formatter that extends SimpleFormatter.
# It replaces "!thread!" with the current thread name
java.util.logging.ConsoleHandler.level=INFO
java.util.logging.ConsoleHandler.formatter=io.helidon.webserver.WebServerLogFormatter
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
----

==== `GreetService.java`: the greeting service for the app
In general, your application can implement multiple services, each tied to its own
URL path. The example includes just one service: the greeting service in
`src/main/java/io/helidon/guides/se/restfulwebservice/GreetService.java`.

. Note these `import` statements.
+
--
[source,java]
// _include::0-9:{greet-service}[tags=importsStart;importsWebServer]
----
import javax.json.Json;
import javax.json.JsonBuilderFactory;
import javax.json.JsonObject;

import io.helidon.common.http.Http;
import io.helidon.config.Config;
import io.helidon.webserver.Routing;
import io.helidon.webserver.ServerRequest;
import io.helidon.webserver.ServerResponse;
import io.helidon.webserver.Service;
----
These imports are necessary for JSON and config support and for the key parts of
the web server.
--
. The `GreetService` class implements `io.helidon.webserver.Service`.
. Its constructor accepts a `Config` object to control its behavior:
+
--
[source,java]
// _include::0-2:{greet-service}[tag=ctor,indent=0]
----
GreetService(Config config) {
    this.greeting = config.get("app.greeting").asString().orElse("Ciao"); //<1>
}
----
Here the code looks up the initial greeting from the configuration object
and saves it.
--
. The `update` method updates the routing rules in the web server to link
the service's methods with the corresponding URL paths:
+
--
[source,java]
// _include::0-6:{greet-service}[tags=update;!updateForCounter,indent=0]
----
@Override
public void update(Routing.Rules rules) {
    rules
        .get("/", this::getDefaultMessageHandler) //<1>
        .get("/{name}", this::getMessageHandler) //<2>
        .put("/greeting", this::updateGreetingHandler); //<3>
}
----
<1> Handle `GET` requests that contain no extra path using `getDefaultMessage`.
<2> Handle `GET` requests that contain a name using `getMessage`, which personalizes the response
using the name provided as the path suffix.
<3> Handle `PUT` requests to the `greeting` path using `updateGreeting`,
interpreting the `greeting` value in the JSON payload as the new greeting string.
--
. The following methods respond to the three types of request.
.. Returning the default greeting:
+
--
[source,java]
// _include::0-3:{greet-service}[tag=getDefaultMessage,indent=0]
----
private void getDefaultMessageHandler(ServerRequest request,
                               ServerResponse response) {
    sendResponse(response, "World"); //<1>
}
----
<1> The default message ends with "World!" -- that is, without personalizing the
message with the user's name.
--
.. Returning a personalized greeting:
+
--
[source,java]
// _include::0-4:{greet-service}[tag=getMessage,indent=0]
----
private void getMessageHandler(ServerRequest request,
                        ServerResponse response) {
    String name = request.path().param("name"); //<1>
    sendResponse(response, name); //<2>
}
----
<1> Gets the name from the URL path in the request.
<2> Includes the user's name in building the response.
--
.. Updating the greeting:
+
--
[source,java]
// _include::0-4:{greet-service}[tag=updateGreeting,indent=0]
----
private void updateGreetingHandler(ServerRequest request,
                                   ServerResponse response) {
    request.content().as(JsonObject.class) // <1>
            .thenAccept(jo -> updateGreetingFromJson(jo, response));
}
----
<1> Compose the JSON response to confirm the new setting for `greeting`.

This method delegates to `updateGreetingFromJson`:
[source,java]
// _include::0-15:{greet-service}[tag=updateGreetingFromJson]
----
    private void updateGreetingFromJson(JsonObject jo, ServerResponse response) {

        if (!jo.containsKey("greeting")) { // <1>
            JsonObject jsonErrorObject = JSON.createObjectBuilder()
                    .add("error", "No greeting provided")
                    .build();
            response.status(Http.Status.BAD_REQUEST_400)
                    .send(jsonErrorObject);
            return;
        }

        greeting = jo.getString("greeting"); // <2>

        response.status(Http.Status.NO_CONTENT_204) // <3>
                .send();
    }
----
<1> Rejects the request if the JSON payload does not include the `greeting` setting.
<2> Extracts the new greeting from the JSON object and saves it.
<3> Sends the "no content" response, acknowledging that the new greeting has been set.
--

==== `Main.java`
The job of `Main` is to create and start the web server. It uses the configuration
in the config file to initialize the server, registering the greeting service with it.
The `startServer` method does most of the work.

. Create and configure the server.
+
--
[source,java]
// _include::0-7:{main-class}[tag=setUpServer,indent=0]
----
// By default this will pick up application.yaml from the classpath
Config config = Config.create(); //<1>

// Get webserver config from the "server" section of application.yaml
ServerConfiguration serverConfig = //<2>
        ServerConfiguration.create(config.get("server"));

WebServer server = WebServer.create(serverConfig, createRouting(config)); //<3>
----
<1> Loads configuration from `application.yaml`.
<2> Creates the `ServerConfiguration` object from the relevant part of the `Config`
object just loaded.
<3> Creates the server using the config and the updated routing rules (see below).
--
. Start the server.
+
--
[source,java]
// _include::0-15:{main-class}[tag=startServer,indent=0]
----
// Try to start the server. If successful, print some info and arrange to
// print a message at shutdown. If unsuccessful, print the exception.
server.start() //<1>
    .thenAccept(ws -> { //<2>
        System.out.println(
                "WEB server is up! http://localhost:" + ws.port() + "/greet");
        ws.whenShutdown().thenRun(() //<3>
            -> System.out.println("WEB server is DOWN. Good bye!"));
        })
    .exceptionally(t -> { //<4>
        System.err.println("Startup failed: " + t.getMessage());
        t.printStackTrace(System.err);
        return null;
    });

// Server threads are not daemon. No need to block. Just react.
----
<1> Starts the server.
<2> When the startup completes successfully, prints a message.
<3> Prints a message when the server is shut down. The `CompletionStage` returned from `server.whenShutdown()` completes when
some other code invokes `server.shutdown()`.
The current example does not
invoke that method (except from a test), so in this example server the
`CompletionStage` will never complete and so the
message will not be printed. This code _does_ show how easy it is to detect and
respond to an orderly shutdown if you trigger one from your app.
<4> Report a failed startup.
--
. Create routing rules for the app.
+
--
[source,java]
// _include::0-13:{main-class}[tags=createRouting;!addCustomHealthCheck,indent=0]
----
private static Routing createRouting(Config config) {

    MetricsSupport metrics = MetricsSupport.create();
    GreetService greetService = new GreetService(config);
    HealthSupport health = HealthSupport.builder()
            .add(HealthChecks.healthChecks())   // Adds a convenient set of checks
            .build(); //<1>
    return Routing.builder() //<2>
            .register(JsonSupport.create())
            .register(health)                   // Health at "/health"
            .register(metrics)                  // Metrics at "/metrics"
            .register("/greet", greetService)
            .build();
}
----
<1> Sets up several built-in health checks (deadlock, disk space, heap memory) for
the server.
<2> Builds the `Routing` instance by registering the JSON, health, metrics, and the
app's own greeting service.

Later steps in this guide show how to add your own, app-specific health check and
metric.
--

== Build and run
// tag::buildAndRun[]
You can use your IDE's features to build and run the project directly.

Or, to use Maven outside the IDE, build your app this way:
[source,bash]
mvn package

and run it like this:
[source,bash,subs="attributes+"]
java -jar target/{artifact-id}.jar

Once you have started your app, from another command window run these commands
to access its functions:
[[curl-command-table]]
|====
|Command |Result |Function

a|[source,bash]
curl -X GET http://localhost:8080/greet
a|[listing]
{"message":"Hello World!"}
|Returns a greeting with no personalization

a|[source,bash]
curl -X GET http://localhost:8080/greet/Joe
a|[listing]
{"message":"Hello Joe!"}
|Returns the personalized greeting

a|[source,bash]
curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Hola"}' http://localhost:8080/greet/greeting
a|
(no response payload)
|Changes the greeting

a|[source,bash]
curl -X GET http://localhost:8080/greet/Jose
a|[listing]
{"message":"Hola Jose!"}
|Shows that the greeting change took effect
|====
// end::buildAndRun[]

== Add an app-specific health check
// tag::addHealthChecksIntro[]
A well-behaved microservice reports on its own health.
Two common approaches for checking health, often used together, are:

- readiness - a simple verification that the service has been started, has initialized itself,
and is ready to respond to requests; and
- liveness - often a more thorough assessment of whether
and how well the service can do its job.

For example, Kubernetes can ping your service's
readiness endpoint after it starts the pod containing the service to determine
when the service is ready to accept requests, withholding traffic until the readiness
endpoint reports success. Kubernetes can use the liveness endpoint to find out if
the service considers itself able to function, attempting a pod restart if the
endpoint reports a problem.

In general a liveness check might assess:

- service health - whether the service itself can do its job correctly
- host health - if the host has sufficient resources (for example, disk space)
for the service to operate
- health of other, dependent services - if other services on which this service
depends are themselves OK.

We will add an app-specific liveness check.
Our greeting service does not depend on any
host resources (like disk space) or any other services. So for this
example we define our service as "alive" in a very trivial way:
if the greeting text has been assigned
_and is not empty_ when trimmed of leading or trailing white space. Otherwise we
consider the service to be unhealthy, in which case the service will
still respond but its answers might not be what we want.

Normally we would
write our service to make
sure that a newly-assigned greeting is non-empty _before_
accepting it. But omitting that validation lets us create an easy health check
that we can use by simply setting the greeting to blank from
a `curl` command.
// end::addHealthChecksIntro[]

=== Revise `GreetService.java`
. Add health-related imports.
+
--
[source,java]
// _include::0-1:{greet-service}[tag=importsHealth]
----
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.HealthCheckResponseBuilder;
----
--
. Add a liveness check method.
+
--
The new method returns a `HealthCheckResponse`. This will make it
very easy to add our custom health check to the built-in ones already in the code.

[source,java]
// _include::0-10:{greet-service}[tag=checkAlive]
----
    HealthCheckResponse checkAlive() {
        HealthCheckResponseBuilder builder = HealthCheckResponse.builder()
                .name("greetingAlive"); //<1>
        if (greeting == null || greeting.trim().length() == 0) { //<2>
            builder.down() //<3>
                   .withData("greeting", "not set or is empty");
        } else {
            builder.up(); //<4>
        }
        return builder.build(); //<5>
    }
----
<1> Use a builder to assemble the response, giving the health check a human-readable
name.
<2> Enforce that the greeting be non-empty and non-null in order for the
greeting service to be considered alive.
<3> For a null or empty greeting the response indicates that the service
is _down_, in this case adding an explanation.
<4> For a valid greeting the response says the service is _up_.
<5> Either way, have the builder build the response.
--

=== Revise `Main.java`
We need to modify the `createRouting` method slightly to add our custom health check to the `HealthSupportBuilder`.
[source,java]
// _include::0-0:{main-class}[tag=addCustomHealthCheck]
----
                .add(greetService::checkAlive)
----

Here's the revised method after this change:

[source,java]
// _include::0-14:{main-class}[tag=createRouting,indent=0]
----
private static Routing createRouting(Config config) {

    MetricsSupport metrics = MetricsSupport.create();
    GreetService greetService = new GreetService(config);
    HealthSupport health = HealthSupport.builder()
            .add(HealthChecks.healthChecks())   // Adds a convenient set of checks
            .add(greetService::checkAlive)
            .build(); //<1>
    return Routing.builder() //<2>
            .register(JsonSupport.create())
            .register(health)                   // Health at "/health"
            .register(metrics)                  // Metrics at "/metrics"
            .register("/greet", greetService)
            .build();
}
----
<1> The `health` instance now includes the greet service liveness check.
<2> The returned routing refers to the previously-instantiated and saved `GreetService`
instance.

// tag::rebuildAndRerunService[]
=== Stop, rebuild and rerun your service

. Stop any running instance of your app.
. Rebuild the app and then run it.

// end::rebuildAndRerunService[]

// tag::tryReadiness[]

=== Check the server's health
Run this command:
[source,bash]
curl -X GET http://localhost:8080/health | json_pp

You should see output as shown in this example:
[listing,subs=+quotes]
----
{
    "checks": [
        {
            "name": "deadlock",
            "state": "UP"
        },
        {
            "data": {
                "free": "179.37 GB",
                "freeBytes": 192597303296,
                "percentFree": "38.51%",
                "total": "465.72 GB",
                "totalBytes": 500068036608
            },
            "name": "diskSpace",
            "state": "UP"
        },
        *{
            "name": "greetingAlive",
            "state": "UP"
        }*,
        {
            "data": {
                "free": "255.99 MB",
                "freeBytes": 268422144,
                "max": "4.00 GB",
                "maxBytes": 4294967296,
                "percentFree": "98.73%",
                "total": "308.00 MB",
                "totalBytes": 322961408
            },
            "name": "heapMemory",
            "state": "UP"
        }
    ],
    "outcome": "UP"
}
----
// tag::se-HealthChecks-notes[]
The JSON output conveys various health indicators because the generated code
included `HealthChecks.healthChecks()` in the `HealthSupport.builder`.
// end::se-HealthChecks-notes[]
The item labeled `outcome` describes the overall health of the
server based on all the other indicators. The state of all the indicators is UP.
So the `outcome` field shows UP. You should also see our app-specific liveness check in the output
(bolded above).

=== Arrange for an unhealthy report
Recall that our simple rule for liveness is that the greeting be non-null and
non-empty. We can easily force our server to report an unhealthy state.

. Set the greeting to a blank.
+
--
[source,bash]
curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : " "}' http://localhost:8080/greet/greeting

Our code to update the greeting accepts this and saves it as the new greeting.
--

. Ping the health check endpoint again with the same command as before.
+
--
[source,bash]
curl -X GET http://localhost:8080/health | python -m json.tool

This time you should see these two parts of the output indicating that something is
wrong:
[listing]
----
        {
            "data": {
                "greeting": "not set or is empty"
            },
            "name": "greetingAlive",
            "state": "DOWN"
        }
...
    "outcome": "DOWN"
----
If you add `-i` to the `curl` command and remove the pipe, the output includes the status 503 "Service Unavailable" report:
[source,bash]
curl -i -X GET http://localhost:8080/health

[listing]
----
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
Date: Tue, 5 Feb 2019 08:09:22 -0600
transfer-encoding: chunked
connection: keep-alive
...
----
--

. Set the greeting back to "Hello", so that the service is healthy again.
+
--
[source,bash]
curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Hello"}' http://localhost:8080/greet/greeting
--

. Check the health again.
+
--
[source,bash]
curl -X GET http://localhost:8080/health | python -m json.tool

This time the `outcome` and `greetingAlive` values will be back to `UP`.
--

// end::tryReadiness[]

== Add metrics support
// tag::metricsIntro[]
As a simple illustration of using metrics, we revise our greeting service to count how many times
a client sends a request to the app.
// end::metricsIntro[]

=== Review default metrics
The generated `Main` class already instantiates and registers `MetricsSupport` in
the `createRouting` method. As a result, the system automatically collects and
reports a number of measurements related to CPU, threads, memory, and request traffic.
Use `curl -X GET http://localhost:8080/metrics` to get the metrics data.

=== Revise `GreetService.java` to add an app-specific metric
. Add metrics-related imports.
+
--
[source,java]
// _include::0-2:{greet-service}[tags=importsHelidonMetrics;importsMPMetrics,indent=0]
----
import io.helidon.metrics.RegistryFactory;
import org.eclipse.microprofile.metrics.Counter;
import org.eclipse.microprofile.metrics.MetricRegistry;
----
--
. Register a metric in `GreetService.java`.
+
--
Add these declarations as private fields:
[source,java]
// _include::0-2:{greet-service}[tags=metricsRegistration;counterRegistration]
----
    private final MetricRegistry registry = RegistryFactory.getInstance()
            .getRegistry(MetricRegistry.Type.APPLICATION); // <1>
    private final Counter greetCounter = registry.counter("accessctr"); // <2>
----
<1> Refers to the application-scoped metrics registry.
<2> Declares a metric of type `counter` with name `accessctr`.
--

. Create a method to display which method is handling a request.
+
--
Add this method:

[source,java]
// _include::0-3:{greet-service}[tag=displayThread]
----
    private void displayThread() {
        String methodName = Thread.currentThread().getStackTrace()[2].getMethodName();
        System.out.println("Method=" + methodName + " " + "Thread=" + Thread.currentThread().getName());
    }
----
--

. Create a request handler to update the counter.
+
--
Add this method:

[source,java]
// _include::0-5:{greet-service}[tag=counterFilter]
----
    private void counterFilter(final ServerRequest request,
                               final ServerResponse response) {
        displayThread(); // <1>
        greetCounter.inc(); // <2>
        request.next(); // <3>
    }
----
<1> Shows which method is handling the request.
<2> Updates the counter metric.
<3> Lets the next handler process the same request.
--

. Register the filter to count requests.
+
--
To the `update` method add this line immediately before the
existing `get` invocations.

[source,java]
// _include::0-0:{greet-service}[tag=updateForCounter,indent=0]
----
.any(this::counterFilter) // <1>
----
<1> Invokes `counterFilter` for _any_ incoming request.
--

=== Rebuild and rerun your application
Follow the same steps as before, remembering to stop any instance
of your application that is still running.

=== Send some requests
Use the same `curl` commands from the beginning to send requests to the server:

|====
|Command |Server Output

a|[source,bash]
curl -X GET http://localhost:8080/greet
a|[listing]
Method=counterFilter Thread=nioEventLoopGroup-3-1

a|[source,bash]
curl -X GET http://localhost:8080/greet/Joe
a|[listing]
Method=counterFilter Thread=nioEventLoopGroup-3-2

a|[source,bash]
curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Hola"}' http://localhost:8080/greet/greeting
a|[listing]
Method=counterFilter Thread=nioEventLoopGroup-3-3

a|[source,bash]
curl -X GET http://localhost:8080/greet/Jose
a|[listing]
Method=counterFilter Thread=nioEventLoopGroup-3-4
|====

=== Retrieve metrics
Run this `curl` command to retrieve the collected metrics:
[source,bash]
----
curl -X GET http://localhost:8080/metrics
----

You should see a long response. Note two items:
|====
|Output |Meaning

a|[listing]
 application:accessctr 4

|The counter we added to the app

a|[listing]
vendor:requests_count 7

|The total number of HTTP requests that the Helidon web server received
|====
The requests count is higher because the access to `/metrics` to retrieve the
monitoring data is _not_ handled by our app's rules and filters but by the
metrics infrastructure.

[[downloading,Download the example source]]
== (Optional) Download the example source
// tag::downloading[]
Instead of generating and then enhancing the application as described in this guide,
you can download it.

. Clone the Helidon repository:
+
--
[source,bash]
.Using ssh
git clone git@github.com:oracle/helidon.git

or
[source,bash]
.Using HTTPS
git clone https://github.com/oracle/helidon.git
--
. `cd` to the `helidon/examples/guides/{artifact-id}` directory.
. Run:
+
--
[source,bash,subs="attributes+"]
----
mvn package
java -jar target/{artifact-id}.jar
----
--
// end::downloading[]
