- Maven 3.5+
- Java 8+
- Docker
- Any kind of Java IDE
-
Create a working folder (ex.
helidon-dubai
) and make it active. -
Generate project using Maven archetype:
mvn archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=io.helidon.archetypes \
-DarchetypeArtifactId=helidon-quickstart-mp \
-DarchetypeVersion=1.3.1 \
-DgroupId=io.helidon.examples \
-DartifactId=helidon-quickstart-mp \
-Dpackage=io.helidon.examples.quickstart.mp
Windows users must make it in one row by deleting backslashes and end of lines
- Open project in your favorite IDE.
cd helidon-quickstart-mp
idea pom.xml
- Build the project
mvn clean package
- Run the project
java -jar target/helidon-quickstart-mp.jar
- Test the project
In another terminal tab:
curl -X GET http://localhost:8080/greet
curl -X GET http://localhost:8080/greet/Dmitry
curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : “Ahoj"}' http://localhost:8080/greet/greeting
curl -X GET http://localhost:8080/greet/Dmitry
- Build a Docker image
docker build -t helidon-quickstart-mp .
- Run project in Docker
docker run --rm -p 8080:8080 helidon-quickstart-mp:latest
- HealthCheck 1.0 endpoint
curl -X GET http://localhost:8080/health | jq .
- HealthCheck 2.0 liveness and readiness checks
curl -X GET http://localhost:8080/health/live | jq .
curl -X GET http://localhost:8080/health/ready | jq .
- Create a new java class:
package io.helidon.examples.quickstart.mp;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Readiness;
import javax.enterprise.context.ApplicationScoped;
@Readiness
@ApplicationScoped
public class StateHealthCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named("state")
.down()
.build();
}
}
- Recompile and rerun the app
mvn clean package -DskipTests
java -jar target/helidon-quickstart-mp.jar
- Test
curl -X GET http://localhost:8080/health/ready | jq .
- Add
app.state
property toMETA-INF\microprofile-config.properties
file.
app.state=Down
- Modify
StateHealthCheck
to read state from configuration
@Readiness
@ApplicationScoped
public class StateHealthCheck implements HealthCheck {
@Inject
@ConfigProperty(name = "app.state")
private String state;
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named(“state")
.state("up".equalsIgnoreCase(state))
.build();
}
}
- Recompile and rerun the app
mvn clean package -DskipTests
java -jar target/helidon-quickstart-mp.jar
- Test
curl -X GET http://localhost:8080/health/ready | jq .
- Pass state using a system property.
java -Dapp.state=Up -jar target/helidon-quickstart-mp.jar
-
Remove
app.state
property fromMETA-INF/microprofile-config.properties
file. -
Create
mp.yaml
configuration file inconf
directory of your working folder. -
Update
mp.yaml
app:
status: "down"
- Crete custom configuration in
Main.java
static Server startServer() {
Config config = Config.builder()
.sources(
file("../conf/mp.yaml")
.pollingStrategy(PollingStrategies::watch)
.optional(),
classpath("META-INF/microprofile-config.properties"))
.build();
return Server.builder().config(config).build().start();
}
- Update
StateHealthCheck
to support dynamic properties
@Readiness
@ApplicationScoped
public class StateHealthCheck implements HealthCheck {
@Inject
@ConfigProperty(name = "app.state")
private Supplier<String> state;
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named("state")
.state("up".equalsIgnoreCase(state.get()))
.build();
}
}
- Recompile and rerun the app
mvn clean package -DskipTests
java -jar target/helidon-quickstart-mp.jar
- Test (must be down)
curl -X GET http://localhost:8080/health/ready | jq .
- Without stopping the app update
mp.yaml
file. Changeapp.state
property toup
.
app:
status: "up"
- Test again (must be UP)
curl -X GET http://localhost:8080/health/ready | jq .
It may take some time to propagate the change. If status is not changed to UP from the first try, re-run the command.
- Metrics in Prometeus format
curl -X GET http://localhost:8080/metrics
- Metrics in JSON
curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics | jq .
- Base metrics only
curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics/base | jq .
- Metrics metadata
curl -H 'Accept: application/json' -X OPTIONS http://localhost:8080/metrics | jq .
- Add
@Counted
metric togetDefaultMessage
method
@GET
@Counted(name = "defaultCount",
absolute = true,
displayName = "Default Message Counter",
description = "Number of times default message called")
@Produces(MediaType.APPLICATION_JSON)
public JsonObject getDefaultMessage() {
return createResponse("World");
}
- Add
@Timed
metric togetMessage
method
@Path("/{name}")
@GET
@Timed
@Produces(MediaType.APPLICATION_JSON)
public JsonObject getMessage(@PathParam("name") String name) {
return createResponse(name);
}
- Recompile and rerun the app
mvn clean package -DskipTests
java -jar target/helidon-quickstart-mp.jar
- Run commands below multiple times to fill metrics data
curl -X GET http://localhost:8080/greet
curl -X GET http://localhost:8080/greet/Dmitry
- Test it
curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics/application | jq .
curl -H 'Accept: application/json' -X OPTIONS http://localhost:8080/metrics/application | jq .
- Run H2 database in docker
docker run -d -p 9092:9082 -p 8082:8082 --name=h2 nemerosa/h2
- Add dependencies
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>2.2.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<version>1.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.helidon.integrations.cdi</groupId>
<artifactId>helidon-integrations-cdi-hibernate</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.helidon.integrations.cdi</groupId>
<artifactId>helidon-integrations-cdi-jpa</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.helidon.integrations.cdi</groupId>
<artifactId>helidon-integrations-cdi-jta-weld</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.helidon.integrations.cdi</groupId>
<artifactId>helidon-integrations-cdi-datasource-hikaricp</artifactId>
<scope>runtime</scope>
</dependency>
- Add
META-INF/persistence.xml
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="test" transaction-type="JTA">
<jta-data-source>test</jta-data-source>
<class>io.helidon.examples.quickstart.mp.Greeting</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<property name="show_sql" value="true"/>
<property name="hibernate.temp.use_jdbc_metadata_defaults" value="false"/>
</properties>
</persistence-unit>
</persistence>
- Configure data source in the application configuration. Add the following to
META-INF/microprofile-config.xml
javax.sql.DataSource.test.dataSourceClassName=org.h2.jdbcx.JdbcDataSource
javax.sql.DataSource.test.dataSource.url=jdbc:h2:mem:test;INIT=CREATE TABLE IF NOT EXISTS GREETING (NAME VARCHAR NOT NULL, GREETING VARCHAR NOT NULL, PRIMARY KEY (NAME))\\;MERGE INTO GREETING (NAME, GREETING) VALUES ('world', 'hello')
javax.sql.DataSource.test.dataSource.user=sa
javax.sql.DataSource.test.dataSource.password=
- Add an entity
Greeting.java
import javax.persistence.*;
import java.util.Objects;
@Access(AccessType.FIELD)
@Entity(name = "Greeting")
@Table(name = "GREETING")
public class Greeting {
@Id
@Column(name = "NAME", insertable = true, nullable = false, updatable = false)
private String name;
@Basic(optional = false)
@Column(name = "GREETING", insertable = true, nullable = false, updatable = true)
private String greeting;
@Deprecated
protected Greeting() {
super();
}
public Greeting(final String name, final String greeting) {
super();
this.name = Objects.requireNonNull(name);
this.greeting = Objects.requireNonNull(greeting);
}
public void setGreeting(final String greeting) {
this.greeting = Objects.requireNonNull(greeting);
}
@Override
public String toString() {
return this.greeting;
}
public String getGreeting() {
return greeting;
}
}
- Modify
GreetResource.createResponse
method
@PersistenceContext
private EntityManager entityManager;
private JsonObject createResponse(String who) {
Greeting greeting = this.entityManager.find(Greeting.class, who);
String message;
if (null == greeting) {
// not in database
message = greetingProvider.getMessage();
} else {
message = greeting.getGreeting();
}
String msg = String.format("%s %s!", message, who);
return JSON.createObjectBuilder()
.add("message", msg)
.build();
}
- Add GreetResource.dbCreateMapping method
@Path("/db/{name}")
@POST
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
@Transactional(Transactional.TxType.REQUIRED)
public Response dbCreateMapping(@PathParam("name") String name, String greeting) {
Greeting g = new Greeting(name, greeting);
this.entityManager.persist(g);
return Response.created(URI.create("/greet/" + name)).build();
}
- Test it
curl -i -X POST -H 'Content-Type:text/plain' -d 'Use' http://localhost:8080/greet/db/helidon
curl -i http://localhost:8080/greet/helidon
- Add GreetResource.dbUpdateMapping method
@Path("/db/{name}")
@PUT
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
@Transactional(Transactional.TxType.REQUIRED)
public Response dbUpdateMapping(@PathParam("name") String name, String greeting) {
try {
Greeting g = this.entityManager.getReference(Greeting.class, name);
g.setGreeting(greeting);
} catch (EntityNotFoundException e) {
return Response.status(404).entity("Mapping for " + name + " not found").build();
}
return Response.ok(name).build();
}
- Test it
curl -i -X PUT -H 'Content-Type:text/plain' -d 'I am using' http://localhost:8080/greet/db/helidon
curl -i http://localhost:8080/greet/helidon
- Create Helidon MP Quickstart project:
mvn archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=io.helidon.archetypes \
-DarchetypeArtifactId=helidon-quickstart-se \
-DarchetypeVersion=1.3.1 \
-DgroupId=io.helidon.examples \
-DartifactId=helidon-quickstart-se \
-Dpackage=io.helidon.examples.quickstart.se
- Open it in IDE of your choice
idea pom.xml
- Build the project
mvn package
- Run the app
java -jar target/helidon-quickstart-se.jar
- In a new terminal window demonstrate that all endpoints work
curl -X GET http://localhost:8080/greet
curl -X GET http://localhost:8080/greet/Dmitry
curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : “Ahoj"}' http://localhost:8080/greet/greeting
curl -X GET http://localhost:8080/greet/Dmitry
- Health check works too
curl -X GET http://localhost:8080/health
- and metrics
curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics | json_pp
-
Download GraalVM 19.2.1 and extract it to
~/graalvm
directory. -
Install
native-image
feature
sudo yum install gcc, zlib-devel
~/graalvm/graalvm-ce-19.2.1/bin/gu install native-image
- Create
GRAALVM_HOME
environment variable
export GRAALVM_HOME=~/graalvm/graalvm-ce-19.2.1
- Build a native image
mvn package -DskipTests -Pnative-image
- Run it
./target/helidon-quickstart-se
- Test endpoints
curl -X GET http://localhost:8080/greet
curl -X GET http://localhost:8080/greet/Dmitry
curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : “Ahoj"}' http://localhost:8080/greet/greeting
curl -X GET http://localhost:8080/greet/Dmitry
- Modify source code the way that app stops after web server initialization
server.start()
.thenAccept(ws -> {
System.exit(0);
System.out.println(
"WEB server is up! http://localhost:" + ws.port() + "/greet");
ws.whenShutdown().thenRun(()
-> System.out.println("WEB server is DOWN. Good bye!"));
})
.exceptionally(t -> {
System.err.println("Startup failed: " + t.getMessage());
t.printStackTrace(System.err);
return null;
});
- Compile and run on JVM
mvn package -DskipTests
time java -jar target/helidon-quickstart-se.jar
- Compile and run on GraalVM
mvn package -DskipTests -Pnative-image
time ./target/helidon-quickstart-se