Skip to content

ederks85/jakarta-ee-microprofile-workshop

Repository files navigation

Building Microservices with Jakarta EE and MicroProfile

Assignments EclipseCon 2019

Part 1: Containerized builds and deployments of Payara Application Server runtimes

The first part of this tutorial will show you how to build Jakarta EE enterprise applications, complemented with MicroProfile for microservices support. The application is a simple “Quote of the Day” showcase, that consists of three services fulfilling their own purpose. All three services will be built and deployed on their own variant of Payara runtime, while showing that the Jakarta EE and MicroProfile programming models don’t change!

Getting Started

Make sure you have prepared this tutorial by following these instructions:

  • Preparation Instructions
  • Import the pom.xml in the root folder as Maven project in your IDE. Verify that you have imported these three projects:
    • application-server-project
    • hollow-jar-project
    • uber-jar-project

Assignment 1: Run an application on an application server runtime (Payara Server Full)

This project covers the following Jakarta EE and MicroProfile components:

  • JAX-RS
  • CDI
  • JPA
  • JSON-B

Navigate to the application-server-project:

cd application-server-project

Add the following dependencies to the pom.xml in this project in order to make it a Jakarta EE 8 Full Profile and MicroProfile 2.2 compatible application:

<dependency>
   <groupId>jakarta.platform</groupId>
   <artifactId>jakarta.jakartaee-api</artifactId>
   <version>8.0.0</version>
   <scope>provided</scope>
</dependency>
<dependency>
   <groupId>org.eclipse.microprofile</groupId>
   <artifactId>microprofile</artifactId>
   <version>2.2</version>
   <type>pom</type>
   <scope>provided</scope>
</dependency>

Note: Payara Server Full 5.193 implements both Jakarta EE 8 and MicroProfile 2.2, so matching these dependencies ensures compatibility of your app and the application server runtime.


Build the project with Maven:

mvn package

Build the Docker image with the application-server-project.war artifact on it:

docker build -t application-server-project .

Run the application in Docker (add the -d parameter for detached mode):

docker-compose up

Note: This command will spin up a Docker container, which starts the Payara application server as a Java process that deploys your app on it.


Open the following URL with a browser or HTTP client to get a random Quote form the running application:

http://localhost:8080/application-server-project/rest/quotes/random

The output should be similar to:

Hello EclipseCon 2019!

In order to retrieve random Quotes, replace the getRandomQuote() method in the QuoteQueries class:

  @GET
  @Path("/random")
  public Response getRandomQuote() throws Exception {
      return Response.ok("Hello EclipseCon 2019!").build();
  }

with:

@GET
@Path("/random")
public Response getRandomQuote() throws Exception {
  // Simulate flaky behaviour
  final long randomId = Double.valueOf(Math.random() * 10d).longValue();

  if ((0L <= randomId) && (4L > randomId)) {
     throw new RuntimeException("Random exception from Quote backend");
  }
  if ((4L <= randomId) && (6L > randomId)) {
     Thread.sleep(10000);
  }
  return Response.ok(this.quotes.getRandomQuote()).build();
}

Note: This method adds some flaky behaviour to this service that is later being handled by another service. To deploy these changes on the running application server, run the following command to rebuild the WAR and have it being picked up and redeployed by the application server.


Rebuild the project with Maven:

mvn package

Note: this will result in the WAR being replaced in the artifact folder that is being monitored by the running application server.


If you call the former URL again:

http://localhost:8080/application-server-project/rest/quotes/random

The output should be similar to:

  • a very slow response, possibly even resulting in an error due to a timeout
  • a random Server exception with HTTP status 500
  • the following JSON response, containing Quote data:
{
"author":"Richard Pattis","id":22,"quote":"When debugging, novices insert corrective code; experts remove defective code. "
}

Note: feel free to refresh this URL to experience the various results.

Note: If the redeploy doesn’t seem to work, clear or remove the “artifact” folder and all it’s deployment information, and try the former steps again


Shut down your running Docker containers in order to finish this assignment:

docker-compose down -v

Assignment 2: Run an application as a Hollow JAR runtime (Payara Micro)

This project covers the following MicroProfile components:

  • JAX-RS
  • MicroProfile RestClient
  • MicroProfile FaultTolerance
    • Retry
    • Fallback
    • Timeout

Navigate to the hollow-jar-project:

cd hollow-jar-project

Add the following dependencies to the pom.xml in this project in order to make it a Jakarta EE 8 Web Profile and MicroProfile 2.2 compatible application:

<dependency>
   <groupId>jakarta.platform</groupId>
   <artifactId>jakarta.jakartaee-web-api</artifactId>
   <version>8.0.0</version>
   <scope>provided</scope>
</dependency>
<dependency>
   <groupId>org.eclipse.microprofile</groupId>
   <artifactId>microprofile</artifactId>
   <version>2.2</version>
   <type>pom</type>
   <scope>provided</scope>
</dependency>

Note: the “web” profile is needed here because Payara Micro supports this profile, and not the “full” Profile


Open the QuoteQueries class in the project and replace the:

  @GET
  @Path("/random/backend")
  public Response getRandomQuote() throws Exception {
      return Response.ok("Hello EclipseCon 2019!").build();
  }

with:

 @GET
 @Path("/random/backend")
 @Retry
 @Fallback(fallbackMethod = "fallback")
 @Timeout(2000)
 public Response getRandomQuote() {
    return Response.ok(quotesBackendClient.getRandomQuoteFromBackend()).build();
 }

Note: this method is annotated with MicroProfile FaultTolerance features to mitigate the flaky behaviour of the application-server-project REST endpoint which it communicates with.


Build the project with Maven:

mvn package

Build the Docker image:

docker build -t hollow-jar-project .

Run the application in Docker (add the -d parameter for detached mode):

docker-compose up

Note: This command will spin a Docker container, which starts the Hollow JAR as a Java process that points to your app that will be deployed on it.


Open the following URL with a browser or HTTP client to get a random Quote form the running application:

http://localhost:9090/hollow-jar-project/rest/quotes/random/backend

The output should be similar to the following JSON response:

{
"author":"Richard Pattis","id":22,"quote":"When debugging, novices insert corrective code; experts remove defective code. "
}

Note: this output is retrieved from a call to the already running backend application-server-project via the hollow-jar service


Shut down your running Docker containers in order to finish this assignment:

docker-compose down

Assignment 3: Run an application as a Uber JAR runtime (Payara Micro)

This project covers the following Jakarta EE and MicroProfile components:

  • JSF
  • CDI
  • MicroProfile RestClient

Navigate to the uber-jar-project:

cd uber-jar-project

Add the following dependencies to the pom.xml in this project in order to make it a Jakarta EE 8 Web Profile and MicroProfile 2.2 compatible application:

<dependency>
   <groupId>jakarta.platform</groupId>
   <artifactId>jakarta.jakartaee-web-api</artifactId>
   <version>8.0.0</version>
   <scope>provided</scope>
</dependency>
<dependency>
   <groupId>org.eclipse.microprofile</groupId>
   <artifactId>microprofile</artifactId>
   <version>2.2</version>
   <type>pom</type>
   <scope>provided</scope>
</dependency>

Note: the “web” profile is needed here because Payara Micro supports this profile, and not the “full” Profile


Also, add the JSF component library “PrimeFaces” dependencies to generate a GUI based on JSF components:

<dependency>
   <groupId>org.primefaces</groupId>
   <artifactId>primefaces</artifactId>
   <version>7.0</version>
</dependency>
<dependency>
  <groupId>org.primefaces.themes</groupId>
  <artifactId>all-themes</artifactId>
  <version>1.0.10</version>
</dependency>

To create the JSF based GUI, add the following XHTML to the index.xhtml file in this project:

<!DOCTYPE html>
<html lang="en"
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://xmlns.jcp.org/jsf/html"
  xmlns:a="http://xmlns.jcp.org/jsf/passthrough"
  xmlns:p="http://primefaces.org/ui"
>
  <h:head>
     <title>Uber JAR example: Payara 5.193</title>
  </h:head>
  <h:body>
     <p:staticMessage severity="info" summary="INFO!" detail="EclipseCon 2019 - Building Microservices with Jakarta EE and MicroProfile" />
     <p:outputLabel ></p:outputLabel>
     <p:dataTable var="quote" value="#{quoteQueries.randomQuotes}">
        <p:column headerText="Id">
           <h:outputText value="#{quote.id}" />
        </p:column>
 
        <p:column headerText="Author">
           <h:outputText value="#{quote.author}" />
        </p:column>
 
        <p:column headerText="Quote">
           <h:outputText value="#{quote.quote}" />
        </p:column>
     </p:dataTable>
  </h:body>
</html>

Note: The namespaces xmlns:h, xmlns:a and xmlns:p provide access to the JSF components that are defined here. Have a look at what else is avaliable! In case you are interested, you can start here: https://www.primefaces.org/showcase/.


Build the project with Maven:

mvn install

Note: this command will download payara-micro:5.193 via Maven. In case this is not working, you can alternatively place payara-micro-5.193.jar from the preparations in the root of the uber-jar-project and run the following commands:

mvn package
java -jar payara-micro-5.193.jar --deploy target/uber-jar-project.war --outputUberJar target/uber-jar-project-microbundle.jar

Build the Docker image with the Uber JAR on it:

docker build -t uber-jar-project .

Note: you may notice that it takes longer to replace the relatively large Uber JAR on the layer of the Docker image. Replacing only the small WAR files on the Docker images like it is done in the former examples is much faster and more efficient. Using an Uber JAR therefore makes the least sense in the context of development .


Run the application in Docker (add the -d parameter for detached mode):

docker-compose up

Note: this command will spin up a Docker container, which starts the Uber JAR as a Java process that contains both the app and Payara Micro.


Open the following URL with a browser or HTTP client to get a random Quote form the running application:

http://localhost:7070/rest/quotes/random/microservice

or

http://localhost:7070/uber-jar-project/rest/quotes/random/microservice

Note: it can apparently vary on which of the former URL's the Uber JAR will be deployed. Therefore, these two options are given where one should match for your situation.


The output should be similar to the following JSON response:

{
"author":"Richard Pattis","id":22,"quote":"When debugging, novices insert corrective code; experts remove defective code. "
}

Note: this output is retrieved from a call to the already running intermediate microservice hollow-jar-project


You can also open the JSF GUI via:

http://localhost:7070/

or

http://localhost:7070/uber-jar-project/

The visual output should be similar to the following:

JSF

Shut down your running Docker containers in order to finish this assignment:

docker-compose down

Part 2: Cloud - local setup

This part will cover deploying the former setup from Part 1 in Minikube. Before you start, make sure you have shut down all running (Docker) services on every project with:

docker-compose down

Assignment 4: Run the application-server-project in Minikube

Open a terminal and start the Kubernetes dashboard:

minikube dashboard

Note: this command will open the Kubernetes dashboard in a browser.


Open a new terminal and start minikube:

minikube start

Note: Minikube appears to run unstable with it's default memory and CPU settings. We recommend settings it to 4 CUP's and 8GB of RAM, but this can be tweaked to meet your system's specifications of course.


Reuse the docker daemon inside Kubernetes:


Note: Windows users: use git bash as your terminal to execute the following commands


eval $(minikube docker-env)

Deploy the database to Kubernetes:

cd application-server-project
kubectl apply -f kubernetes/mysql.yml

Note: this command deploys the mysql service to Kubernetes. This can be seen in the Kubernetes dashboard (in your browser). You should see a Deployment, Pod, Replica Set, and a Service with a name similar to MySQL.


Build the project with Maven:

mvn package

Build the Docker image:

docker build -t application-server-project .

Deploy this service to Kubernetes:

kubectl apply -f kubernetes/application-server-project.yml

Open the exposed endpoint of this service in your browser:

minikube service application-server-project

Note: the last command opens the application-server-project in your browser (don’t worry about the ‘This site can’t be reached’ message, or if you land at the default Payara welcome page. Append ‘/application-server-project/rest/quotes/random’ at the end of the URL in your browser, to get a response from this endpoint.


Assignment 5: Run the hollow-jar-project in Minikube and connect it to the application-server-project

Build and deploy the hollow-jar-project:

cd hollow-jar-project

Build the project with Maven:

mvn package

Build the Docker image:

docker build -t hollow-jar-project .

Deploy this service to Kubernetes:

kubectl apply -f kubernetes/hollow-jar-project.yml

Open the exposed endpoint of this service in your browser:

minikube service hollow-jar-project

Note: the last command opens the hollow-jar-project in your browser (don’t worry about the ‘This site can’t be reached’ message. Append ‘/hollow-jar-project/rest/quotes/random/backend’’ at the end of the URL in your browser, to get a response from this endpoint that calls the application-server-project service to get its data.


Assignment 6: Run the uber-jar-project in Minikube and connect it to the hollow-jar-project

Build and deploy the hollow-jar service:

cd uber-jar-project

Build the project with Maven:

mvn install

Build the Docker image:

docker build -t uber-jar-project .

Deploy this service to Kubernetes:

kubectl apply -f kubernetes/uber-jar-project.yml

Open the exposed endpoint of this service in your browser:

minikube service uber-jar-project

Note: the last command opens the uber-jar-project in your browser (don’t worry about the ‘This site can’t be reached’ message. Otherwise it is likely that it will show the JSF GUI since this is deployed on the root context "/". If you want to access the REST endpoint, append ‘/rest/quotes/random/microservice’ at the end of the URL in your browser, to get a response from this endpoint that calls the hollow-jar-project service to get its data.


Part 3: Scaling and rolling updates

This part will show how to scale your applications in order to handle increased traffic. It also shows how to do zero downtime deployments. Zero downtime deployments enable you to deploy your changed application, without impact on the user experience. Users will not notice that a new application is being deployed, because there will be no downtime!

Assignment 7: Scale the uber-jar-project in Minikube

Your command prompt should be in the uber-jar-project folder. If not, then change to this folder

cd uber-jar-project

Scale the number of replicas of the uber-jar-project to 2:

kubectl scale deployments/uber-jar-project --replicas=2

Open the dashboard in your browser.


Note: you should see that the number of pods of the uber-jar-project is 2.


Assignment 8: Zero downtime deployment

In order to have 2 replicas of a fixed version of the uber-jar-project image and liveness and readiness probes configured, you need to change its Kubernetes configuration.

Change the kubernetes/uber-jar-project.yml Kubernetes configuration from

apiVersion: v1
kind: Service
metadata:
 name: uber-jar-project
 labels:
   app: uber-jar-project
spec:
 type: NodePort
 ports:
   - port: 8080
 selector:
   app: uber-jar-project
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: uber-jar-project
 labels:
   app: uber-jar-project
spec:
 replicas: 1
 selector:
   matchLabels:
     app: uber-jar-project
 template:
   metadata:
     labels:
       app: uber-jar-project
   spec:
     containers:
       - name: uber-jar-project
         image: uber-jar-project:latest
         imagePullPolicy: IfNotPresent
         ports:
           - containerPort: 8080

to

apiVersion: v1
kind: Service
metadata:
 name: uber-jar-project
 labels:
   app: uber-jar-project
spec:
 type: NodePort
 ports:
   - port: 8080
 selector:
   app: uber-jar-project
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: uber-jar-project
 labels:
   app: uber-jar-project
spec:
 replicas: 2
 selector:
   matchLabels:
     app: uber-jar-project
 template:
   metadata:
     labels:
       app: uber-jar-project
   spec:
     containers:
       - name: uber-jar-project
         image: uber-jar-project:v1
         imagePullPolicy: IfNotPresent
         ports:
           - containerPort: 8080
         livenessProbe:
           httpGet:
             path: /health
             port: 8080
           initialDelaySeconds: 60
           failureThreshold: 3
           periodSeconds: 10
         readinessProbe:
           httpGet:
             path: /health
             port: 8080
           initialDelaySeconds: 30
           failureThreshold: 10
           periodSeconds: 3

Notes:

  • The difference is in the number of replicas (was 1, now 2) and the image tag (was latest, now v1).
  • The livenessProbe is used by Kubernetes to check whether the container is still ‘live’. This is done by a http GET request call to the configured liveness endpoint (‘/health’). The first request is done after a delay of 60 seconds (initialDelaySeconds), to give the container some time to boot. If the response of this call is successful, then the container is still ‘live’. If the response is not successful for 3 times in total (failureTreshold) with a period of 10 seconds in between the calls (periodSeconds), then the container is not ‘live’ anymore, and will be killed by Kubernetes and a new pod will be started.
  • The readinessProbe is used by Kubernetes to check whether the container is ‘ready’, to accept traffic. This is done by a http GET request call to the configured readiness endpoint (‘/health’). The first request is done after a delay of 30 seconds (initialDelaySeconds), to give the container some time to boot. If the response of this call is successful, then the service is ‘ready’ to accept traffic. If the response is not successful, for 10 times in total (failureTreshold) with a period of 3 seconds in between the calls (periodSeconds), then the container was not ‘ready’ and will be killed by Kubernetes and a new pod will be started.

Now let’s build version 1 of the uber-jar-project by rolling out the changed Kubernetes configuration.

Tag the docker image to v1

docker build -t uber-jar-project:v1 .

Deploy this version (v1) with 2 replicas to Kubernetes

kubectl apply -f kubernetes/uber-jar-project.yml

Open the dashboard in your browser.


Note: you should see that the number of pods of the uber-jar-project is still 2.


In order to make it a visible change to the uber-jar-project, you need to change the theme of the uber-jar-project. Change the value of the context-param primefaces.THEME from vader to bootstrap in the web.xml file. This changes the black background color of the table:

JSF

to a white background color:

JSF

Change the theme in the web.xml file from ‘vader’:

<context-param>  
  <param-name>primefaces.THEME</param-name>
  <param-value>vader</param-value>  
</context-param>

to 'bootstrap':

<context-param>  
  <param-name>primefaces.THEME</param-name>
  <param-value>bootstrap</param-value>
</context-param>

Rebuild the project with Maven:

mvn clean install

Build the Docker image:

docker build -t uber-jar-project:v2 .

Change the image version in the uber-jar-project Kubernetes configuration from ‘v1’:

         image: uber-jar-project:v1

to ‘v2’:

         image: uber-jar-project:v2

Deploy this new version (v2)

kubectl apply -f kubernetes/uber-jar-project.yml

Note: now the new version gets deployed. While deploying, the old versions are used to serve requests. When a new version is up, an old version is terminated and will not receive any traffic anymore. In this situation, 1 new and 1 old pod are running. This means that some requests will be served by a new version and some by an old version. Then the second new version will be started up. When this new pod is ready, then the last old pod will be terminated and will not receive requests anymore. Now 2 new pods are running that serve all the request. You should only see the new version of the uber-jar-project when you refresh your screen (white background color in the table).


About

A workshop on how to build microservices using Jakarta EE and MicroProfile

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published