Chapter 4. Develop Eclipse MicroProfile Applications for JBoss EAP

4.1. Maven and the JBoss EAP Maven repository

4.1.1. The Maven repository

Apache Maven is a distributed build automation tool used in Java application development to create, manage, and build software projects. Maven uses standard configuration files called Project Object Model, or POM, files to define projects and manage the build process.

A POM file is an XML file that contains information about the project and how to build it, including the location of the source, test, and target directories, the project dependencies, plug-in repositories, and goals it can execute.

The minimum requirements of a pom.xml file are as follows:

  • project root
  • modelVersion
  • groupId - the ID of the project’s group
  • artifactId - the ID of the artifact (project)
  • version - the version of the artifact under the specified group

Example: Basic pom.xml file

The following example demonstrates a basic pom.xml file:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.jboss.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
</project>

A Maven repository stores Java libraries, plug-ins, and other build artifacts. The default public repository is the Maven 2 Central Repository, but repositories can be private and internal within a company with a goal to share common artifacts among development teams. Repositories are also available from third-parties. JBoss EAP includes a Maven repository that contains many of the requirements that Jakarta EE developers typically use to build applications on JBoss EAP.

Whenever an expansion pack is released for JBoss EAP, a corresponding patch is provided for the JBoss EAP Maven repository. This patch is provided as an incremental archive file that is extracted into the existing Red Hat JBoss Enterprise Application Platform 7.3.1. GA Maven repository. The incremental archive file does not overwrite or remove any existing files, so there is no rollback requirement.

Additional Resources

4.1.2. Downloading the JBoss EAP Maven repository patch as an archive file

Whenever an expansion pack is released for JBoss EAP, a corresponding patch is provided for the JBoss EAP Maven repository. This patch is provided as an incremental archive file that is extracted into the existing Red Hat JBoss Enterprise Application Platform 7.3.1. GA Maven repository. The incremental archive file does not overwrite or remove any existing files, so there is no rollback requirement.

Prerequisites

Procedure

  1. Open a browser and log in to the Red Hat Customer Portal.
  2. Select Downloads from the menu at the top of the page.
  3. Find the Red Hat JBoss Enterprise Application Platform entry in the list and select it.
  4. From the Product drop-down list, select JBoss EAP XP.
  5. From the Version drop-down list, select 1.0.0.
  6. Click the Releases tab.
  7. Find JBoss EAP XP 1.0.0 Incremental Maven Repository in the list, and then click Download.
  8. Save the archive file to your local directory.

Additional Resources

4.1.3. Applying the JBoss EAP Maven repository patch on your local system

You can install the JBoss EAP Maven repository patch on your local file system.

When you apply a patch in the form of an incremental archive file to the repository, new files are added to this repository. The incremental archive file does not overwrite or remove any existing files on the repository, so there is no rollback requirement.

Prerequisites

  • You have set up an account on the Red Hat Customer Portal.
  • You have installed Maven.
  • You have downloaded and installed the Red Hat JBoss Enterprise Application Platform 7.3.1. GA Maven repository on your local system.

    • Check that you have this minor version of the Red Hat JBoss Enterprise Application Platform 7.3 Maven repository installed on your local system.
  • You have downloaded the JBoss EAP XP 1.0.0 expansion pack patch on your local system.

Procedure

  1. Locate the path to your JBoss EAP Maven repository. This is referred to in the commands in this section as EAP_MAVEN_REPOSITORY_PATH. For example, jboss-eap-7.3.1.GA-maven-repository.
  2. Extract the downloaded Maven patch file directly into the directory of the JBoss EAP Maven repository. For example, open a terminal and issue the following command, replacing the value for your Maven repository path:

    $ unzip -o jboss-eap-xp-1.0.0-incremental-maven-repository.zip -d /EAP_MAVEN_REPOSITORY_PATH
Important

If you want to use an older local repository, you must configure it separately in the Maven settings.xml configuration file. Each local repository must be configured within its own <repository> tag.

Additional Resources

4.1.4. Configuring the JBoss EAP Maven repository with the Maven settings

Maven settings used with a repository manager or repository on a shared server provide better control and manageability of projects. You can configure Maven settings to use an alternative mirror to redirect all lookup requests for a specific repository to your repository manager without changing the project files.

If the project POM file does not contain a repository configuration, you can configure the settings to apply across all Maven projects.

Prerequisite

  • You have downloaded and installed the Red Hat JBoss Enterprise Application Platform 7.3 Maven repository on your local system.
Note

You can access the jboss-eap-xp-microprofile BOM after you install the JBoss EAP Maven repository. This BOM is shipped inside the JBoss EAP Maven repository.

Procedure

You can configure the Maven install global settings or the user install settings.

  1. Locate the Maven settings.xml file for your operating system. It is usually located in the ${user.home}/.m2/ directory.
  2. Optional: If you do not find a settings.xml file, copy the settings.xml file from the ${user.home}/.m2/conf/ directory into the ${user.home}/.m2/ directory.
  3. Copy the following XML into the <profiles> element of the settings.xml file. Determine the URL of the JBoss EAP repository and replace JBOSS_EAP_REPOSITORY_URL with it.

    <!-- Configure the JBoss Enterprise Maven repository -->
    <profile>
     <id>jboss-enterprise-maven-repository</id>
     <repositories>
       <repository>
         <id>jboss-enterprise-maven-repository</id>
         <url>JBOSS_EAP_REPOSITORY_URL</url>
         <releases>
           <enabled>true</enabled>
         </releases>
         <snapshots>
           <enabled>false</enabled>
         </snapshots>
       </repository>
     </repositories>
     <pluginRepositories>
       <pluginRepository>
         <id>jboss-enterprise-maven-repository</id>
         <url>JBOSS_EAP_REPOSITORY_URL</url>
         <releases>
           <enabled>true</enabled>
         </releases>
         <snapshots>
           <enabled>false</enabled>
         </snapshots>
       </pluginRepository>
     </pluginRepositories>
    </profile>
  4. The following is an example configuration that accesses the online JBoss EAP Maven repository.

    <!-- Configure the JBoss Enterprise Maven repository -->
    <profile>
     <id>jboss-enterprise-maven-repository</id>
     <repositories>
       <repository>
         <id>jboss-enterprise-maven-repository</id>
         <url>https://maven.repository.redhat.com/ga/</url>
         <releases>
           <enabled>true</enabled>
         </releases>
         <snapshots>
           <enabled>false</enabled>
         </snapshots>
       </repository>
     </repositories>
     <pluginRepositories>
       <pluginRepository>
         <id>jboss-enterprise-maven-repository</id>
         <url>https://maven.repository.redhat.com/ga/</url>
         <releases>
           <enabled>true</enabled>
         </releases>
         <snapshots>
           <enabled>false</enabled>
         </snapshots>
       </pluginRepository>
     </pluginRepositories>
    </profile>
  5. Copy the following XML into the <activeProfiles> element of the settings.xml file.

    <activeProfile>jboss-enterprise-maven-repository</activeProfile>
  6. Optional: If you modify the settings.xml file while Red Hat CodeReady Studio is running, you must refresh the user settings.

    1. From the menu, choose WindowPreferences.
    2. In the Preferences window, expand Maven and choose User Settings.
    3. Click the Update Settings button to refresh the Maven user settings in Red Hat CodeReady Studio.
Important

If your Maven repository contains outdated artifacts, you might encounter one of the following Maven error messages when you build or deploy your project:

  • Missing artifact ARTIFACT_NAME
  • [ERROR] Failed to execute goal on project PROJECT_NAME; Could not resolve dependencies for PROJECT_NAME

To resolve the issue, delete the cached version of your local repository to force a download of the latest Maven artifacts. The cached repository is located at the ${user.home}/.m2/repository/ path.

Additional Resources

4.1.5. Configuring the JBoss EAP Maven repository with the POM file

You must plan carefully if you decide to configure repositories using the project POM file.

Warning

Configuring the JBoss EAP Maven repository with the POM file method overrides the global and user Maven settings for the configured project.

Transitively including POMs becomes an issue with this type of configuration, as Maven must query the external repositories for missing artifacts. This slows the build process and can cause you to lose control over where your artifacts originated.

Prerequisites

  • You have installed the JBoss EAP Maven repository.
  • You know the location of the repository; that is, either on your file system or a web server.
Note

When you have installed the JBoss EAP Maven repository, you can then access the jboss-eap-xp-microprofile BOM.

Note

The URL of the repository depends on where the repository is located: on the file system or the web server. The following examples show the types of installation options available to you:

File System
file:///path/to/repo/jboss-eap-maven-repository
Apache Web Server
http://intranet.acme.com/jboss-eap-maven-repository/
Nexus Repository Manager
https://intranet.acme.com/nexus/content/repositories/jboss-eap-maven-repository

Procedure

  1. Open your project’s pom.xml file in a text editor.
  2. Add the following repository configuration. If the <repositories> configuration in the file already exists, then add the <repository> element to it.
  3. Change the <url> to the actual repository location.

    <repositories>
       <repository>
          <id>jboss-eap-repository-group</id>
          <name>JBoss EAP Maven Repository</name>
          <url>JBOSS_EAP_REPOSITORY_URL</url>
          <layout>default</layout>
          <releases>
             <enabled>true</enabled>
             <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
             <enabled>true</enabled>
             <updatePolicy>never</updatePolicy>
          </snapshots>
       </repository>
    </repositories>
  4. Add the following plug-in repository configuration. If a <pluginRepositories> configuration in the file already exists, then add the <pluginRepository> element to it.

    <pluginRepositories>
       <pluginRepository>
          <id>jboss-eap-repository-group</id>
          <name>JBoss EAP Maven Repository</name>
          <url>JBOSS_EAP_REPOSITORY_URL</url>
          <releases>
             <enabled>true</enabled>
          </releases>
          <snapshots>
             <enabled>true</enabled>
          </snapshots>
       </pluginRepository>
    </pluginRepositories>

Additional Resources

4.1.6. Configuring the JBoss EAP Maven repository with the Red Hat CodeReady Studio

You can add the JBoss EAP Maven repository to the Red Hat CodeReady Studio application. When the repository is added, you can use application graphical user interface to configure the repository.

Prerequisites

  • You have downloaded and installed Apache Maven.
  • You have installed theJBoss EAP Maven repository.
  • You know the location of the repository; that is, either on your file system or a web server.

Procedure

  1. From the graphical user interface, click WindowPreferences, expand JBoss Tools and select JBoss Maven Integration.
  2. Click Configure Maven Repositories.
  3. Click Add Repository to configure the JBoss EAP Maven repository. Complete the Add Maven Repository dialog as follows:

    1. Set the Profile ID, Repository ID, and Repository Name values to jboss-ga-repository.
    2. Set the Repository URL value to http://maven.repository.redhat.com/ga.
    3. Click the Active by default checkbox to enable the Maven repository.
    4. Click OK.
  4. Review the repositories and click Finish.
  5. You are prompted with the message "Are you sure you want to update the file MAVEN_HOME/settings.xml?". Click Yes to update the settings ,and then click OK to close the dialog.

The JBoss EAP Maven repository is now configured for use with Red Hat CodeReady Studio.

Additional Resources

4.1.7. JBoss EAP BOM for Eclipse MicroProfile applications

A Bill of Materials (BOM) is a Maven pom.xml (POM) file that specifies information about the project. A BOM lists configuration details used by Maven to build the project.

When you add the BOM to your project pom.xml file, you must then specify the following:

  • The <groupId>:<artifactId>:<version> (GAV) properties for the BOM in the management section of the file.
  • The <scope>import</scope> element value.
  • The <type>pom</type> element value.

Typically, a Maven build uses a mix of artifact sources from the Maven central repository and other Maven repositories. However, by you adding a JBoss EAP public supported Eclipse MicroProfile artifact to the pom.xml file ensures that the build uses the correct binary artifact for local building and testing.

As part of the build process, all runtime components of JBoss EAP are built from source in a controlled environment. This helps to ensure that the binary artifacts do not contain any malicious code, and that they can be supported for the life of the product.

The wildfly-microprofile BOM provides a way to work with all the APIs that are supported by Eclipse MicroProfile implementations. A similar BOM exists for JBoss EAP: the jboss-eap-xp-microprofile BOM. You can access the jboss-eap-xp-microprofile BOM after you install the JBoss EAP Maven repository.

You can add JBoss EAP supported Eclipse MicroProfile binary artifacts to the build by using the dependency management Eclipse MicroProfile BOM, which is located in the JBoss EAP Maven repository. When you use this BOM, Maven prioritizes supported Eclipse MicroProfile dependencies for all direct and transitive dependencies in the build.

Additional Resources

4.1.8. Managing Eclipse MicroProfile dependencies

Maven uses a Project Object Model (POM) configuration file to build projects. For JBoss EAP, a BOM is a Maven POM file that specifies the versions of all runtime dependencies for a given module. Version dependencies are listed in the dependency management section of this BOM file. You can use this BOM to add JBoss EAP supported Eclipse MicroProfile binary artifacts to a project build.

Additionally, you can use the JBoss EAP Maven repository to build applications on JBoss EAP.

Prerequisites

  • You have installed Maven.
  • You have installed the JBoss EAP Maven repository.

    • When you have installed the JBoss EAP Maven repository, you can then access the jboss-eap-xp-microprofile BOM.

Procedure

  1. Configure the project POM file by specifying a repository element and the online JBoss EAP repository web address for Maven:

    <repositories>
       <repository>
          <id>jboss-eap-repository-group</id>
          <name>JBoss EAP Maven Repository</name>
          <url>https://maven.repository.redhat.com/ga_</url>
          <layout>default</layout>
          <releases>
             <enabled>true</enabled>
             <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
             <enabled>true</enabled>
             <updatePolicy>never</updatePolicy>
          </snapshots>
       </repository>
    </repositories>
    Note

    Remote repositories are accessed using common protocols such as http:// for a repository on an HTTP server, or file:// for a repository on a file server.

  2. Add the jboss-eap-xp-microprofile BOM in the dependency Management section of the project pom.xml file.

    The jboss-eap-xp-microprofile BOM, whose groupId is org.jboss.bom, packages many JBoss EAP supported Eclipse MicroProfile API dependencies, such as microprofile-openapi-api and microprofile-config-api. Therefore, you do not need to separately add these supported dependencies to the project pom.xml file.

  3. Specify the <groupId>:<artifactId>:<version> (GAV) properties, <scope> element, and type element for the file. The following example shows specified properties values and elements values in a`pom.xml` file:

    <dependencyManagement>
      <dependencies>
        ...
        <dependency>
          <groupId>org.jboss.bom</groupId>
          <artifactId>jboss-eap-xp-microprofile</artifactId>
          <version>1.0.0.GA</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
        ...
      </dependencies>
    </dependencyManagement>
    Note

    If you do not specify a value for the type element in the pom.xml file, Maven specifies a jar value for the element.

  4. Add the Eclipse MicroProfile dependencies to the jboss-eap-xp-microprofile BOM. You do not need to specify a version nor a scope, because the jboss-eap-xp-microprofile BOM specifies these values for the dependencies. The following example uses the 1.0.0.GA version of the jboss-eap-xp-microprofile BOM to add dependencies for the MicroProfile Open API:

    <dependencies>
      <dependency>
        <groupId>org.eclipse.microprofile.openapi</groupId>
        <artifactId>microprofile-openapi-api</artifactId>
        <scope>provided</scope>
      </dependency>

    Additional Resources

4.1.9. Supported JBoss EAP Eclipse MicroProfile BOM

JBoss EAP XP 1.0.0 includes one Eclipse MicroProfile BOM. This BOM is jboss-eap-xp-microprofile and its use case supports JBoss EAP Jakarta EE 8 APIs and JBoss EAP API JARs.

Table 4.1. JBoss EAP Eclipse MicroProfile BOM

BOM Artifact IDUse Case

jboss-eap-xp-microprofile

BOM, whose groupId is org.jboss.bom, packages many JBoss EAP supported API dependencies, such as microprofile-openapi-api and microprofile-config-api. Therefore, you do not need to separately add these supported dependencies to the project pom.xml file.

Additional Resources

4.2. Eclipse MicroProfile Config development

4.2.1. Creating a Maven project

Procedure

  1. Set up the Maven project.

    $ mvn archetype:generate \
        -DgroupId=com.example \
        -DartifactId=microprofile-config \
        -DinteractiveMode=false \
        -DarchetypeGroupId=org.apache.maven.archetypes \
        -DarchetypeArtifactId=maven-archetype-webapp
    cd microprofile-config

    This creates the directory structure for the project and pom.xml configuration file.

  2. Configure dependencies in the pom.xml file.

    <!-- Import the MicroProfile Config API -->
    <dependency>
      <groupId>org.eclipse.microprofile.config</groupId>
      <artifactId>microprofile-config-api</artifactId>
      <scope>provided</scope>
    </dependency>
    <!-- Import the CDI API -->
    <dependency>
      <groupId>jakarta.enterprise</groupId>
      <artifactId>jakarta.enterprise.cdi-api</artifactId>
      <scope>provided</scope>
    </dependency>
    <!-- Import the Jakarta REST API -->
    <dependency>
      <groupId>org.jboss.spec.javax.ws.rs</groupId>
      <artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
      <scope>provided</scope>
    </dependency>
  3. Add Eclipse MicroProfile Config dependency.

    <dependencyManagement>
      <dependencies>
        <!-- importing the microprofile BOM adds MicroProfile specs -->
        <dependency>
            <groupId>org.wildfly.bom</groupId>
            <artifactId>wildfly-microprofile</artifactId>
            <version>${version.server.bom}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
      </dependencies>
    </dependencyManagement>

    Replace ${version.server.bom} with the installed version of BOM.

  4. Create a Maven plug-in for the project.

    <build>
      <!-- Set the name of the archive -->
      <finalName>${project.artifactId}</finalName>
      <plugins>
        <!-- Allows to use mvn wildfly:deploy -->
        <plugin>
          <groupId>org.wildfly.plugins</groupId>
          <artifactId>wildfly-maven-plugin</artifactId>
        </plugin>
      </plugins>
    </build>

    Replace ${project.artifactId} with the required name for the application.

4.2.2. Using MicroProfile Config property in an application

Create an application that uses a configured ConfigSource.

Prerequisites

  • Eclipse MicroProfile Config is enabled in JBoss EAP.
  • The latest POM is installed.

Procedure

  1. Create a class file HelloService.java with the following content:

    package com.example.microprofile.config;
    
    public class HelloService {
        String createHelloMessage(String name){
            return "Hello" + name;
        }
    }
  2. Create a class file HelloWorld.java with the following content:

    package com.example.microprofile.config;
    
    import javax.inject.Inject;
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import org.eclipse.microprofile.config.inject.ConfigProperty;
    
    @Path("/")
    public class HelloWorld {
    
        @Inject
        @ConfigProperty(name="name") 1
        String name;
    
       	@Inject
       	HelloService helloService;
    
       	@GET
       	@Path("/json")
       	@Produces({ "application/json" })
       	public String getHelloWorldJSON() {
            String message = helloService.createHelloMessage(name);
           	return "{\"result\":\"" + message + "\"}";
    	}
    }
    1
    A MicroProfile Config property is injected in the class with the annotation @ConfigProperty(name="name").
  3. Build the project:

    $ mvn clean install wildfly:deploy
  4. Test the output:

    $ curl http://localhost:8080/config-example/rest/json

    The following is the expected output:

    {"result":"Hello jim!"}

4.3. Eclipse MicroProfile Fault Tolerance application development

4.3.1. Adding the MicroProfile Fault Tolerance extension

The MicroProfile Fault Tolerance extension is included in standalone-microprofile.xml and standalone-microprofile-ha.xml configurations that are provided as part of JBoss EAP XP.

The extension is not included in the standard standalone.xml configuration. To use the extension, you must manually enable it.

Prerequisites

  • EAP XP pack is installed.

Procedure

  1. Add the MicroProfile Fault Tolerance extension using the following management CLI command:

    /extension=org.wildfly.extension.microprofile.fault-tolerance-smallrye:add
  2. Enable the microprofile-fault-tolerance-smallrye subsystem using the following managenent command:

    /subsystem=microprofile-fault-tolerance-smallrye:add
  3. Reload the server with the following management command:

    reload

4.3.2. Configuring Maven project for Eclipse MicroProfile Fault Tolerance

Create a Maven project with the required dependencies and the directory structure for creating an Eclipse MicroProfile Fault Tolerance application.

Prerequisites

  • Maven is installed.

Procedure

  1. Initialize the project:

    mvn archetype:generate \
        -DgroupId=com.example.microprofile.faulttolerance \
        -DartifactId=microprofile-fault-tolerance \
        -DarchetypeGroupId=org.apache.maven.archetypes \
        -DarchetypeArtifactId=maven-archetype-webapp \
        -DinteractiveMode=false
    cd microprofile-fault-tolerance

    The command creates the directory structure for the project and the pom.xml configuration file.

  2. Edit the pom.xml configuration file to contain the following information:

    <?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>
    
        <groupId>com.example.microprofile.faulttolerance
        </groupId>
        <artifactId>microprofile-fault-tolerance</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
    
        <name>microprofile-fault-tolerance Maven Webapp</name>
        <!-- Change the URL to the URL of your website -->
        <url>http://www.example.com</url>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.eclipse.microprofile.fault-tolerance</groupId>
                <artifactId>microprofile-fault-tolerance-api</artifactId>
                <scope>provided</scope>
                <version>2.1</version>
            </dependency>
            <dependency>
                <groupId>jakarta.enterprise</groupId>
                <artifactId>jakarta.enterprise.cdi-api</artifactId>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.jboss.resteasy</groupId>
                <artifactId>resteasy-jaxrs</artifactId>
                <scope>provided</scope>
                <version>3.11.2.Final</version>
            </dependency>
          <dependency>
              <groupId>org.jboss.logging</groupId>
              <artifactId>jboss-logging</artifactId>
              <scope>provided</scope>
              <version>3.4.1.Final</version>
           </dependency>
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
              <!-- importing the microprofile BOM adds MicroProfile specs -->
              <dependency>
                 <groupId>org.wildfly.bom</groupId>
                 <artifactId>wildfly-microprofile</artifactId>
                 <version>19.1.0.Final</version>
                 <type>pom</type>
                 <scope>import</scope>
              </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <finalName>microprofile-fault-tolerance</finalName>
            <pluginManagement>
                <!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
                <plugins>
                    <plugin>
                        <artifactId>maven-clean-plugin</artifactId>
                        <version>3.1.0</version>
                    </plugin>
                    <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                    <plugin>
                        <artifactId>maven-resources-plugin</artifactId>
                        <version>3.0.2</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <version>3.8.0</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-surefire-plugin</artifactId>
                        <version>2.22.1</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-war-plugin</artifactId>
                        <version>3.2.2</version>
                    </plugin>
                    <plugin>
                       <artifactId>maven-install-plugin</artifactId>
                       <version>2.5.2</version>
                    </plugin>
                    <plugin>
                        <artifactId>maven-deploy-plugin</artifactId>
                        <version>2.8.2</version>
                    </plugin>
                    <!-- Allows to use mvn wildfly:deploy -->
                    <plugin>
                        <groupId>org.wildfly.plugins</groupId>
                       <artifactId>wildfly-maven-plugin</artifactId>
                    </plugin>
                </plugins>
            </pluginManagement>
        </build>
    
    </project>

Use the pom.xml configuration file and directory structure to create an application.

4.3.3. Creating a fault tolerant application

Create a fault-tolerant application that implements retry, timeout, and fallback patterns for fault tolerance.

Prerequisites

  • Maven dependencies have been configured.

Procedure

  1. Create the directory to store class files:

    $ mkdir -p APPLICATION_ROOT/src/main/java/com/example/microprofile/faulttolerance

    APPLICATION_ROOT is the directory containing the pom.xml configuration file for the application.

  2. Navigate to the new directory:

    $ cd APPLICATION_ROOT/src/main/java/com/example/microprofile/faulttolerance

    For the following steps, create all class files in the new directory.

  3. Create a simple entity representing a coffee sample as Coffee.java with the following content:

    package com.example.microprofile.faulttolerance;
    
    public class Coffee {
    
        public Integer id;
        public String name;
        public String countryOfOrigin;
        public Integer price;
    
        public Coffee() {
        }
    
        public Coffee(Integer id, String name, String countryOfOrigin, Integer price) {
            this.id = id;
            this.name = name;
            this.countryOfOrigin = countryOfOrigin;
            this.price = price;
        }
    }
  4. Create a class file CoffeeApplication.java with the following content:

    package com.example.microprofile.faulttolerance;
    
    import javax.ws.rs.ApplicationPath;
    import javax.ws.rs.core.Application;
    
    @ApplicationPath("/")
    public class CoffeeApplication extends Application {
    }
  5. Create a CDI Bean as CoffeeRepositoryService.java with the following content:

    package com.example.microprofile.faulttolerance;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;
    import javax.enterprise.context.ApplicationScoped;
    
    @ApplicationScoped
    public class CoffeeRepositoryService {
    
        private Map<Integer, Coffee> coffeeList = new HashMap<>();
    
        public CoffeeRepositoryService() {
            coffeeList.put(1, new Coffee(1, "Fernandez Espresso", "Colombia", 23));
            coffeeList.put(2, new Coffee(2, "La Scala Whole Beans", "Bolivia", 18));
            coffeeList.put(3, new Coffee(3, "Dak Lak Filter", "Vietnam", 25));
        }
    
        public List<Coffee> getAllCoffees() {
            return new ArrayList<>(coffeeList.values());
        }
    
        public Coffee getCoffeeById(Integer id) {
            return coffeeList.get(id);
        }
    
        public List<Coffee> getRecommendations(Integer id) {
            if (id == null) {
                return Collections.emptyList();
            }
            return coffeeList.values().stream()
                    .filter(coffee -> !id.equals(coffee.id))
                    .limit(2)
                    .collect(Collectors.toList());
        }
    }
  6. Create a class file CoffeeResource.java with the following content:

    package com.example.microprofile.faulttolerance;
    
    import java.util.List;
    import java.util.Random;
    import java.util.concurrent.atomic.AtomicLong;
    import javax.inject.Inject;
    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;
    import javax.ws.rs.PathParam;
    import org.eclipse.microprofile.faulttolerance.Fallback;
    import org.eclipse.microprofile.faulttolerance.Timeout;
    import org.eclipse.microprofile.faulttolerance.Retry;
    
    @Path("/coffee")
    @Produces(MediaType.APPLICATION_JSON)
    public class CoffeeResource {
    
        @Inject
        private CoffeeRepositoryService coffeeRepository;
    
        private AtomicLong counter = new AtomicLong(0);
    
        @GET
        @Retry(maxRetries = 4) 1
        public List<Coffee> coffees() {
            final Long invocationNumber = counter.getAndIncrement();
            return coffeeRepository.getAllCoffees();
        }
    
    
        @GET
        @Path("/{id}/recommendations")
        @Timeout(250) 2
        public List<Coffee> recommendations(@PathParam("id") int id) {
                return coffeeRepository.getRecommendations(id);
            }
    
        @GET
        @Path("fallback/{id}/recommendations")
        @Fallback(fallbackMethod = "fallbackRecommendations") 3
        public List<Coffee> recommendations2(@PathParam("id") int id) {
            return coffeeRepository.getRecommendations(id);
            }
    
        public List<Coffee> fallbackRecommendations(int id) {
            //always return a default coffee
            return Collections.singletonList(coffeeRepository.getCoffeeById(1));
        }
    }
    1
    Define number of re-tries to 4.
    2
    Define the timeout interval in milliseconds.
    3
    Define a fallback method to call when invocation fails.
  7. Navigate to the root directory of the application:

    $ cd APPLICATION_ROOT
  8. Build the application using the following Maven command:

    $ mvn clean install wildfly:deploy

    Access the application at http://localhost:8080/microprofile-fault-tolerance/coffee.

Additional Resources

  • For a detailed example of fault tolerant application, which includes artificial failures to test the fault tolerance of the application, see the microprofile-fault-tolerance quickstart.

4.4. Eclipse MicroProfile Health development

4.4.1. Custom health check example

The default implementation provided by the microprofile-health-smallrye subsystem performs a basic health check. For more detailed information, on either the server or application status, custom health checks may be included. Any CDI beans that include the org.eclipse.microprofile.health.Health annotation at the class level are automatically discovered and invoked at runtime.

The following example demonstrates how to create a new implementation of a health check that returns an UP state.

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

@Health
public class HealthTest implements HealthCheck {

    @Override
    public HealthCheckResponse call() {
        return HealthCheckResponse.named("health-test").up().build();
    }
}

Once deployed, any subsequent health check queries include the custom checks, as demostrated in the following example.

/subsystem=microprofile-health-smallrye:check
{
    "outcome" => "success",
    "result" => {
        "outcome" => "UP",
        "checks" => [{
            "name" => "health-test",
            "state" => "UP"
        }]
    }
}

4.4.2. The @Liveness annotation example

The following is an example of using the @Liveness annotation in an application.

@Liveness
@ApplicationScoped
public class DataHealthCheck implements HealthCheck {

    @Override
    public HealthCheckResponse call() {
        return HealthCheckResponse.named("Health check with data")
            .up()
            .withData("foo", "fooValue")
            .withData("bar", "barValue")
            .build();
    }
}

4.4.3. The @Readiness annotation example

The following example demonstrates checking connection to a database. If the database is down, the readiness check reports error.

@Readiness
@ApplicationScoped
public class DatabaseConnectionHealthCheck implements HealthCheck {

    @Inject
    @ConfigProperty(name = "database.up", defaultValue = "false")
    private boolean databaseUp;

    @Override
    public HealthCheckResponse call() {

        HealthCheckResponseBuilder responseBuilder = HealthCheckResponse.named("Database connection health check");

        try {
            simulateDatabaseConnectionVerification();
            responseBuilder.up();
        } catch (IllegalStateException e) {
            // cannot access the database
            responseBuilder.down()
                .withData("error", e.getMessage()); // pass the exception message
        }

        return responseBuilder.build();
    }


}

4.5. Eclipse MicroProfile JWT application development

4.5.1. Enabling microprofile-jwt-smallrye subsystem

The Eclipse MicroProfile JWT integration is provided by the microprofile-jwt-smallrye subsystem and is included in the default configuration. If the subsystem is not present in the default configuration, you can add it as follows.

Prerequisites

  • EAP XP is installed.

Procedure

  1. Enable the MicroProfile JWT smallrye extension in JBoss EAP:

    /extension=org.wildfly.extension.microprofile.jwt-smallrye:add
  2. Enable the microprofile-jwt-smallrye subsystem:

    /subsystem=microprofile-jwt-smallrye:add
  3. Reload the server:

    reload

The microprofile-jwt-smallrye subsystem is enabled.

4.5.2. Creating a Maven project for developing JWT applications

Create a Maven project with the required dependencies and the directory structure for developing a JWT application.

Prerequisites

  • Maven is installed.
  • microprofile-jwt-smallrye subsystem is enabled.

Procedure

  1. Initialize the maven project by issuing the following Maven command:

    $ mvn archetype:generate -DinteractiveMode=false \
        -DarchetypeGroupId=org.apache.maven.archetypes \
        -DarchetypeArtifactId=maven-archetype-webapp \
        -DgroupId=com.example -DartifactId=microprofile-jwt \
        -Dversion=1.0.0.Alpha1-SNAPSHOT
      cd microprofile-jwt

    The Maven command creates the directory structure required for developing an application and creates a pom.xml configuration file.

  2. Configure dependencies by adding the following content to pom.xml:

    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>org.wildfly.bom</groupId>
          <artifactId>wildfly-jakartaee8-with-tools</artifactId>
          <version>${version.jboss.bom}</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
        <dependency>
          <groupId>org.wildfly.bom</groupId>
          <artifactId>wildfly-microprofile</artifactId>
          <version>${version.jboss.bom}</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
      </dependencies>
    </dependencyManagement>

    Replace ${version.jboss.bom} with the JBoss EAP version.

  3. Add Eclipse MicroProfile JWT dependency by adding the following content to pom.xml:

    <dependency>
      <groupId>org.jboss.spec.javax.annotation</groupId>
      <artifactId>jboss-annotations-api_1.3_spec</artifactId>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>jakarta.enterprise</groupId>
      <artifactId>jakarta.enterprise.cdi-api</artifactId>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.jboss.spec.javax.ws.rs</groupId>
      <artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.eclipse.microprofile.jwt</groupId>
      <artifactId>microprofile-jwt-auth-api</artifactId>
      <scope>provided</scope>
    </dependency>
  4. Create a Maven plugin for the project by adding the following content to pom.xml

    <build>
      <!-- Set the name of the archive -->
      <finalName>${project.artifactId}</finalName>
      <plugins>
        <!-- Allows to use mvn wildfly:deploy -->
        <plugin>
          <groupId>org.wildfly.plugins</groupId>
          <artifactId>wildfly-maven-plugin</artifactId>
        </plugin>
      </plugins>
    </build>

The Maven project is set up for developing Eclipse MicroProfile JWT applications.

4.5.3. Creating an application with Eclipse MicroProfile JWT

Create an application that authenticates requests based on JWT tokens and implements authorization based on the identity of the token bearer.

Note

The following procedure provides code for generating tokens as an example. You should implement your own token generator.

Prerequisites

  • Maven project is configured with the correct dependencies.

Procedure

  1. Create a token generator.

    This step serves as a reference. For a production environment, implement your own token generator.

    1. Create a directory src/test/java for token the generator utility and navigate to it:

      $ mkdir -p src/test/java
      $ cd src/test/java
    2. Create a class file TokenUtil.java with the following content:

      package  com.example.mpjwt;
      
      import java.io.FileInputStream;
      import java.io.InputStream;
      import java.nio.charset.StandardCharsets;
      import java.security.KeyFactory;
      import java.security.PrivateKey;
      import java.security.spec.PKCS8EncodedKeySpec;
      import java.util.Base64;
      import java.util.UUID;
      
      import javax.json.Json;
      import javax.json.JsonArrayBuilder;
      import javax.json.JsonObjectBuilder;
      
      import com.nimbusds.jose.JOSEObjectType;
      import com.nimbusds.jose.JWSAlgorithm;
      import com.nimbusds.jose.JWSHeader;
      import com.nimbusds.jose.JWSObject;
      import com.nimbusds.jose.JWSSigner;
      import com.nimbusds.jose.Payload;
      import com.nimbusds.jose.crypto.RSASSASigner;
      
      public class TokenUtil {
      
          private static PrivateKey loadPrivateKey(final String fileName) throws Exception {
              try (InputStream is = new FileInputStream(fileName)) {
                  byte[] contents = new byte[4096];
                  int length = is.read(contents);
                  String rawKey = new String(contents, 0, length, StandardCharsets.UTF_8)
                          .replaceAll("-----BEGIN (.*)-----", "")
                          .replaceAll("-----END (.*)----", "")
                          .replaceAll("\r\n", "").replaceAll("\n", "").trim();
      
                  PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(rawKey));
                  KeyFactory keyFactory = KeyFactory.getInstance("RSA");
      
                  return keyFactory.generatePrivate(keySpec);
              }
          }
      
          public static String generateJWT(final String principal, final String birthdate, final String...groups) throws Exception {
          	PrivateKey privateKey = loadPrivateKey("private.pem");
      
              JWSSigner signer = new RSASSASigner(privateKey);
              JsonArrayBuilder groupsBuilder = Json.createArrayBuilder();
              for (String group : groups) { groupsBuilder.add(group); }
      
              long currentTime = System.currentTimeMillis() / 1000;
              JsonObjectBuilder claimsBuilder = Json.createObjectBuilder()
                      .add("sub", principal)
                      .add("upn", principal)
                      .add("iss", "quickstart-jwt-issuer")
                      .add("aud", "jwt-audience")
                      .add("groups", groupsBuilder.build())
                      .add("birthdate", birthdate)
                      .add("jti", UUID.randomUUID().toString())
                      .add("iat", currentTime)
                      .add("exp", currentTime + 14400);
      
              JWSObject jwsObject = new JWSObject(new JWSHeader.Builder(JWSAlgorithm.RS256)
                      .type(new JOSEObjectType("jwt"))
                      .keyID("Test Key").build(),
                      new Payload(claimsBuilder.build().toString()));
      
              jwsObject.sign(signer);
      
              return jwsObject.serialize();
          }
      
          public static void main(String[] args) throws Exception {
              if (args.length < 2) throw new IllegalArgumentException("Usage TokenUtil {principal} {birthdate} {groups}");
              String principal = args[0];
              String birthdate = args[1];
              String[] groups = new String[args.length - 2];
              System.arraycopy(args, 2, groups, 0, groups.length);
      
              String token = generateJWT(principal, birthdate, groups);
              String[] parts = token.split("\\.");
              System.out.println(String.format("\nJWT Header - %s", new String(Base64.getDecoder().decode(parts[0]), StandardCharsets.UTF_8)));
              System.out.println(String.format("\nJWT Claims - %s", new String(Base64.getDecoder().decode(parts[1]), StandardCharsets.UTF_8)));
              System.out.println(String.format("\nGenerated JWT Token \n%s\n", token));
          }
      }
  2. Create the web.xml file in the src/main/webapp/WEB-INF directory with the following content:

    <context-param>
        <param-name>resteasy.role.based.security</param-name>
        <param-value>true</param-value>
    </context-param>
    
    <security-role>
        <role-name>Subscriber</role-name>
    </security-role>
  3. Create a class file SampleEndPoint.java with the following content:

    package com.example.mpjwt;
    
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    
    import java.security.Principal;
    import javax.ws.rs.core.Context;
    import javax.ws.rs.core.SecurityContext;
    
    import javax.annotation.security.RolesAllowed;
    import javax.inject.Inject;
    
    import java.time.LocalDate;
    import java.time.Period;
    import java.util.Optional;
    
    import org.eclipse.microprofile.jwt.Claims;
    import org.eclipse.microprofile.jwt.Claim;
    
    import org.eclipse.microprofile.jwt.JsonWebToken;
    
    @Path("/Sample")
    public class SampleEndPoint {
    
        @GET
        @Path("/helloworld")
        public String helloworld(@Context SecurityContext securityContext) {
            Principal principal = securityContext.getUserPrincipal();
            String caller = principal == null ? "anonymous" : principal.getName();
    
            return "Hello " + caller;
        }
    
        @Inject
    	JsonWebToken jwt;
    
    	@GET()
    	@Path("/subscription")
    	@RolesAllowed({"Subscriber"})
    	public String helloRolesAllowed(@Context SecurityContext ctx) {
        	Principal caller =  ctx.getUserPrincipal();
        	String name = caller == null ? "anonymous" : caller.getName();
        	boolean hasJWT = jwt.getClaimNames() != null;
        	String helloReply = String.format("hello + %s, hasJWT: %s", name, hasJWT);
    
        	return helloReply;
    	}
    
    	@Inject
    	@Claim(standard = Claims.birthdate)
    	Optional<String> birthdate;
    
    	@GET()
    	@Path("/birthday")
    	@RolesAllowed({ "Subscriber" })
    	public String birthday() {
        	if (birthdate.isPresent()) {
            	LocalDate birthdate = LocalDate.parse(this.birthdate.get().toString());
            	LocalDate today = LocalDate.now();
            	LocalDate next = birthdate.withYear(today.getYear());
            	if (today.equals(next)) {
                	return "Happy Birthday";
            }
            if (next.isBefore(today)) {
                next = next.withYear(next.getYear() + 1);
            }
    
            Period wait = today.until(next);
    
            return String.format("%d months and %d days until your next birthday.", wait.getMonths(), wait.getDays());
        }
    
        return "Sorry, we don't know your birthdate.";
    
    	}
    
    }

    The methods annotated with @Path are the JAX-RS endpoints.

    The annotation @Claim defines a JWT claim.

  4. Create a class file App.java to enable JAX-RS:

    package com.example.mpjwt;
    
    import javax.ws.rs.ApplicationPath;
    import javax.ws.rs.core.Application;
    
    import org.eclipse.microprofile.auth.LoginConfig;
    
    @ApplicationPath("/rest")
    @LoginConfig(authMethod="MP-JWT", realmName="MP JWT Realm")
    public class App extends Application {}

    The annotation @LoginConfig(authMethod="MP-JWT", realmName="MP JWT Realm") enables JWT RBAC during deployment.

  5. Compile the application with the following Maven command:

    $ mvn package
  6. Generate JWT token using the token generator utility:

    $ mvn exec:java -Dexec.mainClass=org.wildfly.quickstarts.mpjwt.TokenUtil -Dexec.classpathScope=test -Dexec.args="testUser 2017-09-15 Echoer Subscriber"
  7. Build and deploy the application using the following Maven command:

    $ mvn package wildfly:deploy
  8. Test the application.

    • Call the Sample/subscription endpoint using the bearer token:

      $ curl -H "Authorization: Bearer ey..rg" http://localhost:8080/microprofile-jwt/rest/Sample/subscription
    • Call the Sample/birthday endpoint:

      $ curl -H "Authorization: Bearer ey..rg" http://localhost:8080/microprofile-jwt/rest/Sample/birthday

4.6. Eclipse MicroProfile Metrics development

4.6.1. Creating an Eclipse MicroProfile Metrics application

Create an application that returns the number of requests made to the application.

Procedure

  1. Create a class file HelloService.java with the following content:

    package com.example.microprofile.metrics;
    
    public class HelloService {
        String createHelloMessage(String name){
            return "Hello" + name;
        }
    }
  2. Create a class file HelloWorld.java with the following content:

    package com.example.microprofile.metrics;
    
    import javax.inject.Inject;
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import org.eclipse.microprofile.metrics.annotation.Counted;
    
    @Path("/")
    public class HelloWorld {
    @Inject
        HelloService helloService;
    
    @GET
    @Path("/json")
        @Produces({ "application/json" })
        @Counted(name = "requestCount",
      		 absolute = true,
    description = "Number of times the getHelloWorldJSON was requested")
        public String getHelloWorldJSON() {
            return "{\"result\":\"" + helloService.createHelloMessage("World") + "\"}";
        }
    }
  3. Update the pom.xml file to include the following dependency:

    <dependency>
        <groupId>org.eclipse.microprofile.metrics</groupId>
        <artifactId>microprofile-metrics-api</artifactId>
        <scope>provided</scope>
    </dependency>
  4. Build the application using the following Maven command:

    $ mvn clean install wildfly:deploy
  5. Test the metrics:

    1. Issue the following command in the CLI:

      $ curl -v http://localhost:9990/metrics |  grep request_count | grep helloworld-rs-metrics

      Expected output:

      jboss_undertow_request_count_total{deployment="helloworld-rs-metrics.war",servlet="org.jboss.as.quickstarts.rshelloworld.JAXActivator",subdeployment="helloworld-rs-metrics.war",microprofile_scope="vendor"} 0.0
    2. In a browser, navigate to the URL http://localhost:8080/helloworld-rs/rest/json.
    3. Re-Issue the following command in the CLI:

      $ curl -v http://localhost:9990/metrics |  grep request_count | grep helloworld-rs-metrics

      Expected output:

      jboss_undertow_request_count_total{deployment="helloworld-rs-metrics.war",servlet="org.jboss.as.quickstarts.rshelloworld.JAXActivator",subdeployment="helloworld-rs-metrics.war",microprofile_scope="vendor"} 1.0

4.7. Developing an Eclipse MicroProfile OpenAPI application

4.7.1. Enabling Eclipse MicroProfile OpenAPI

The microprofile-openapi-smallrye subsystem is provided in the standalone-microprofile.xml configuration. However, JBoss EAP XP uses the standalone.xml by default. You must include the subsystem in standalone.xml to use it.

Alternatively, you can follow the procedure Updating standalone configurations with Eclipse MicroProfile subsystems and extensions to update the standalone.xml configuration file.

Procedure

  1. Enable the MicroProfile OpenAPI smallrye extension in JBoss EAP:

    /extension=org.wildfly.extension.microprofile.openapi-smallrye:add()
  2. Enable the microprofile-openapi-smallrye subsystem using the following management command:

    /subsystem=microprofile-openapi-smallrye:add()
  3. Reload the server.

    reload

The microprofile-openapi-smallrye subsystem is enabled.

4.7.2. Configuring Maven project for Eclipse MicroProfile OpenAPI

Create a Maven project to set up the dependencies for creating an Eclipse MicroProfile OpenAPI application.

Prerequisites

  • Maven is installed.

Procedure

  1. Initialize the project:

    mvn archetype:generate \
         -DgroupId=com.example.microprofile.openapi \
         -DartifactId=microprofile-openapi\
         -DarchetypeGroupId=org.apache.maven.archetypes \
         -DarchetypeArtifactId=maven-archetype-webapp \
         -DinteractiveMode=false
    cd microprofile-openapi

    The command creates the directory structure for the project and the pom.xml configuration file.

  2. Edit the pom.xml configuration file to contain:

    <?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>
    
        <groupId>com.example.microprofile.openapi</groupId>
        <artifactId>microprofile-openapi</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
    
        <name>microprofile-openapi Maven Webapp</name>
    	<!-- Update the value with the URL of the project -->
        <url>http://www.example.com</url>
    
    	<!-- Required for processing lambda expressions -->
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <version.server.bom>19.0.0.Final</version.server.bom>
        </properties>
    
        <dependencyManagement>
            <dependencies>
                <!-- importing the microprofile BOM adds MicroProfile specs -->
                <dependency>
                    <groupId>org.wildfly.bom</groupId>
                    <artifactId>wildfly-microprofile</artifactId>
                    <version>${version.server.bom}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <dependencies>
            <dependency>
                <groupId>org.jboss.spec.javax.ws.rs</groupId>
                <artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
                <scope>provided</scope>
            </dependency>
        </dependencies>
    
        <build>
            <!-- Set the name of the archive -->
            <finalName>${project.artifactId}</finalName>
            <plugins>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.2.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
                <!-- Allows to use mvn wildfly:deploy -->
                <plugin>
                    <groupId>org.wildfly.plugins</groupId>
                    <artifactId>wildfly-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>

    Replace ${version.server.bom} with 1.0.0.GA.

    Replace ${project.artifactId} with the name of the application you are creating.

Use the pom.xml configuration file and directory structure to create an application.

4.7.3. Creating an Eclipse MicroProfile OpenAPI application

Create an application that returns an OpenAPI v3 document.

Prerequisites

  • Maven project is configured for creating an Eclipse MicroProfile OpenAPI application.

Procedure

  1. Create the directory to store class files:

    $ mkdir -p APPLICATION_ROOT/src/main/java/com/example/microprofile/openapi/

    APPLICATION_ROOT is the directory containing the pom.xml configuration file for the application.

  2. Navigate to the new directory:

    $ cd APPLICATION_ROOT/src/main/java/com/example/microprofile/openapi/

    All the class files in the following steps must be created in this directory.

  3. Create the class file InventoryApplication.java with the following content:

    package com.example.microprofile.openapi;
    
    import javax.ws.rs.ApplicationPath;
    import javax.ws.rs.core.Application;
    
    @ApplicationPath("/inventory")
    public class InventoryApplication extends Application {
    }

    This class serves as the REST endpoint for the application.

  4. Create a class file Fruit.java with the following content:

    package com.example.microprofile.openapi;
    
    public class Fruit {
    
        private final String name;
        private final String description;
    
        public Fruit(String name, String description) {
            this.name = name;
            this.description = description;
        }
    
        public String getName() {
            return this.name;
        }
    
        public String getDescription() {
            return this.description;
        }
    }
  5. Create a class file FruitResource.java with the following content:

    package com.example.microprofile.openapi;
    
    import java.util.Collections;
    import java.util.LinkedHashMap;
    import java.util.Set;
    
    import javax.ws.rs.Consumes;
    import javax.ws.rs.DELETE;
    import javax.ws.rs.GET;
    import javax.ws.rs.POST;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
    
    @Path("/fruit")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public class FruitResource {
    
        private final Set<Fruit> fruits = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>()));
    
        public FruitResource() {
            this.fruits.add(new Fruit("Apple", "Winter fruit"));
            this.fruits.add(new Fruit("Pineapple", "Tropical fruit"));
        }
    
        @GET
        public Set<Fruit> all() {
            return this.fruits;
        }
    
        @POST
        public Set<Fruit> add(Fruit fruit) {
            this.fruits.add(fruit);
            return this.fruits;
        }
    
        @DELETE
        public Set<Fruit> remove(Fruit fruit) {
            this.fruits.removeIf(existingFruit -> existingFruit.getName().contentEquals(fruit.getName()));
            return this.fruits;
        }
    }
  6. Navigate to the root directory of the application:

    $ cd __APPLICATION_ROOT__
  7. Build and deploy the application using the following Maven command:

    $ mvn wildfly:deploy
  8. Test the application.

    • Access the OpenAPI documentation of the sample application using curl:

      $ curl http://localhost:8080/openapi
    • The following output is returned:

      openapi: 3.0.1
      info:
        title: Store inventory
        description: Application for tracking store inventory
        version: "1.0"
      servers:
      - url: /microprofile-openapi
      paths:
        /inventory/fruit:
          get:
            responses:
              "200":
                description: OK
                content:
                  application/json:
                    schema:
                      type: array
                      items:
                        $ref: '#/components/schemas/Fruit'
          post:
            requestBody:
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/Fruit'
            responses:
              "200":
                description: OK
                content:
                  application/json:
                    schema:
                      type: array
                      items:
                        $ref: '#/components/schemas/Fruit'
          delete:
            requestBody:
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/Fruit'
            responses:
              "200":
                description: OK
                content:
                  application/json:
                    schema:
                      type: array
                      items:
                        $ref: '#/components/schemas/Fruit'
      components:
        schemas:
          Fruit:
            type: object
            properties:
              description:
                type: string
              name:
                type: string

Additional Resources

4.7.4. Configuring JBoss EAP to serve a static OpenAPI document

Configure JBoss EAP to serve a static OpenAPI document that describes the REST services for the host.

When JBoss EAP is configured to serve a static OpenAPI document, JBoss EAP does not process JAX-RS and MicroProfile OpenAPI annotations every time an application is deployed.

In a production environment, disable annotation processing when serving a static document. Disabling annotation processing ensures that an immutable and versioned API contract is available for clients.

Procedure

  1. Create a directory in the application source tree:

    $ mkdir src/main/webapp/META-INF
  2. Query the OpenAPI endpoint, redirecting the output to a file:

    $ curl http://localhost:8080/openapi?format=JSON > src/main/webapp/META-INF/openapi.json

    By default, the endpoint serves a YAML document, format=JSON specifies that a JSON document is returned.

  3. Configure the application to skip annotation scanning when processing the OpenAPI document model:

    $ echo "mp.openapi.scan.disable=true" > src/main/webapp/META-INF/application.properties

JBoss EAP now serves a static OpenAPI document at the OpenAPI endpoint.

4.8. Eclipse MicroProfile REST Client development

4.8.1. A comparison between MicroProfile REST client and JAX-RS syntaxes

The MicroProfile REST client enables a version of distributed object communication, which is also implemented in CORBA, Java Remote Method Invocation (RMI), the JBoss Remoting Project, and RESTEasy. For example, consider the resource:

@Path("resource")
public class TestResource {
   @Path("test")
   @GET
   String test() {
      return "test";
   }
 }

The following example demonstrates using the JAX-RS native way to access the TestResource class:

Client client = ClientBuilder.newClient();
String response = client.target("http://localhost:8081/test").request().get(String.class);

However, Microprofile REST client supports a more intuitive syntax by directly calling the test() method as the following example demonstrates:

@Path("resource")
public interface TestResourceIntf {
    @Path("test")
    @GET
    public String test();
}

TestResourceIntf service = RestClientBuilder.newBuilder()
                              .baseUrl(http://localhost:8081/))
                              .build(TestResourceIntf.class);
String s = service.test();

In the preceding example, making calls on the TestResource class becomes much easier with the TestResourceIntf class, as illustrated by the call service.test().

The following example is a more elaborate version of the TestResourceIntf class:

@Path("resource")
public interface TestResourceIntf2 {
   @Path("test/{path}")mes("text/plain")
   @Produces("text/html")
   @POST
   public String test(@PathParam("path") String path, @QueryParam("query") String query, String entity);
}

Calling the service.test("p", "q", "e") method results in an HTTP message as shown in the following example:

POST /resource/test/p/?query=q HTTP/1.1
Accept: text/html
Content-Type: text/plain
Content-Length: 1

e

4.8.2. Programmatic registration of providers in MicroProfile REST client

With the MicroProfile REST client, you can configure the client environment by registering providers. For example:

TestResourceIntf service = RestClientBuilder.newBuilder()
                              .baseUrl(http://localhost:8081/))
                              .register(MyClientResponseFilter.class)
                              .register(MyMessageBodyReader.class)
                              .build(TestResourceIntf.class);

4.8.3. Declarative registration of providers in MicroProfile REST client

Use the MicroProfile REST client to register providers declaratively by adding the org.eclipse.microprofile.rest.client.annotation.RegisterProvider annotation to the target interface, as shown in the following example:

@Path("resource")
@RegisterProvider(MyClientResponseFilter.class)
@RegisterProvider(MyMessageBodyReader.class)
public interface TestResourceIntf2 {
   @Path("test/{path}")
   @Consumes("text/plain")
   @Produces("text/html")
   @POST
   public String test(@PathParam("path") String path, @QueryParam("query") String query, String entity);
}

Declaring the MyClientResponseFilter class and the MyMessageBodyReader class with annotations eliminates the need to call the RestClientBuilder.register() method.

4.8.4. Declarative specification of headers in MicroProfile REST client

You can specify a header for an HTTP request in the following ways:

  • By annotating one of the resource method parameters.
  • By declaratively using the org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam annotation.

The following example illustrates setting a header by annotating one of the resource method parameters with the annotation @HeaderValue:

@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.TEXT_PLAIN)
String contentLang(@HeaderParam(HttpHeaders.CONTENT_LANGUAGE) String contentLanguage, String subject);

The following example illustrates setting a header using the org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam annotation:

@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.TEXT_PLAIN)
@ClientHeaderParam(name=HttpHeaders.CONTENT_LANGUAGE, value="{getLanguage}")
String contentLang(String subject);

default String getLanguage() {
   return ...;
}

4.8.5. Propagation of headers on the server in MicroProfile REST client

An instance of org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory, if activated, can do a bulk transfer of incoming headers to an outgoing request. The default instance org.eclipse.microprofile.rest.client.ext.DefaultClientHeadersFactoryImpl returns a map consisting of those incoming headers that are listed in the comma-separated configuration property org.eclipse.microprofile.rest.client.propagateHeaders.

Follow these rules when instantiating the ClientHeadersFactory interface:

  • A ClientHeadersFactory instance invoked in the context of a JAX-RS request must support injection of fields and methods annotated with @Context.
  • A ClientHeadersFactory instance that is managed by CDI must use the appropriate CDI-managed instance. It must also support the @Inject injection.

The org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory interface is defined as follows:

public interface ClientHeadersFactory {

/**
 * Updates the HTTP headers to send to the remote service. Note that providers
 * on the outbound processing chain could further update the headers.
 *
 * @param incomingHeaders - the map of headers from the inbound JAX-RS request. This will
 * be an empty map if the associated client interface is not part of a JAX-RS request.
 * @param clientOutgoingHeaders - the read-only map of header parameters specified on the
 * client interface.
 * @return a map of HTTP headers to merge with the clientOutgoingHeaders to be sent to
 * the remote service.
 */
MultivaluedMap<String, String> update(MultivaluedMap<String, String> incomingHeaders,
                                      MultivaluedMap<String, String> clientOutgoingHeaders);
}

Additional resources

4.8.6. ResponseExceptionMapper in MicroProfile REST client

The org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper class is the client-side inverse of the javax.ws.rs.ext.ExceptionMapper class, which is defined in JAX-RS. The ExceptionMapper.toResponse() method turns an Exception class thrown during the server-side processing into a Response class. The ResponseExceptionMapper.toThrowable() method turns a Response class received on the client-side with an HTTP error status into an Exception class.

You can register the ResponseExceptionMapper class either programmatically or declaratively. In the absence of a registered ResponseExceptionMapper class, a default ResponseExceptionMapper class maps any response with status >= 400 to a WebApplicationException class.

4.8.7. Context dependency injection with MicroProfile REST client

In MicroProfile REST client, you must annotate any interface that is managed as a CDI bean with the @RegisterRestClient class. For example:

@Path("resource")
@RegisterProvider(MyClientResponseFilter.class)
public static class TestResourceImpl {
      @Inject TestDataBase db;

      @Path("test/{path}")
      @Consumes("text/plain")
      @Produces("text/html")
      @POST
      public String test(@PathParam("path") String path, @QueryParam("query")
      String query, String entity) {
         return db.getByName(query);
      }
   }
   @Path("database")
   @RegisterRestClient
   public interface TestDataBase {

      @Path("")
      @POST
      public String getByName(String name);
   }

Here, the MicroProfile REST client implementation creates a client for a TestDataBase class service, allowing easy access by the TestResourceImpl class. However, it does not include the information about the path to the TestDataBase class implementation. This information can be supplied by the optional @RegisterProvider parameter baseUri:

@Path("database")
@RegisterRestClient(baseUri="https://localhost:8080/webapp")
public interface TestDataBase {
   @Path("")
   @POST
   public String getByName(String name);
}

This indicates that you can access the implementation of TestDataBase at https://localhost:8080/webapp. You can also supply the information externally with the following system variable:

<fully qualified name of TestDataBase>/mp-rest/url=<URL>

For example, the following command indicates that you can access an implementation of the com.bluemonkeydiamond.TestDatabase class at https://localhost:8080/webapp:

com.bluemonkeydiamond.TestDatabase/mp-rest/url=https://localhost:8080/webapp