Monitoring

Home

Testing


© 2019-2022 rxmicro.io. Free use of this software is granted under the terms of the Apache License 2.0.

Copies of this entity may be made for Your own use and for distribution to others, provided that You do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

If You find errors or omissions in this entity, please don’t hesitate to submit an issue or open a pull request with a fix.

1. Unnamed Modules Support

On September 21, 2017, Java 9 has introduced JPMS.

There are four types of modules in the JPMS:

  • System Modules - There are the Java SE and JDK modules.
    (For example java.base, java.logging, java.net.http, etc)

  • Application Modules - The default modules, that are built using JPMS.
    They are named and defined in the compiled module-info.class file included in the assembled JAR.

  • Automatic Modules - The modules that are created automatically from all unmodularized jar files at the module path. The name of the modules will be derived from the name of the appropriate JAR files.

  • Unnamed Modules - The modules that are created automatically if JAR files are loaded onto the classpath instead of the module path. Unnamed modules disable the JPMS and allow to run any code that does not support the JPMS on Java 9 or higher.

Currently, the majority of existing frameworks and libraries are focused on JDK 8 and do not support JPMS.

To support compatibility with libraries that do not support JPMS, Java 9 provides the unnamed modules mechanism.

The feature of this mechanism is that the code written in Java 9 and higher does not use the module-info.java module descriptor, and uses class-path instead of module-path. For integration with libraries that do not support JPMS, the RxMicro framework allows You to enable the unnamed module mode for the RxMicro Annotation Processor.

To enable this mode, You need to add the RX_MICRO_BUILD_UNNAMED_MODULE configuration parameter to the maven-compiler-plugin settings:

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>${maven-compiler-plugin.version}</version>
    <configuration>
        <release>11</release>
        <!-- ... -->
        <compilerArgs>
            <arg>-ARX_MICRO_BUILD_UNNAMED_MODULE=true</arg> (1)
        </compilerArgs>
    </configuration>
    <!-- ... -->
</plugin>
1 The RxMicro Annotation Processor will use the unnamed module mode for the current microservice project.

1.1. Uber Jar

An uber JAR (also known as a fat JAR or JAR with dependencies) is a JAR file that contains not only a Java program, but embeds its dependencies as well. This means that the JAR functions as an "all-in-one" distribution of the software, without needing any other Java code.

(You still need a Java runtime, and an underlying operating system, of course.)

To build a microservice project in the form of uber.jar, You need to enable the unnamed module mode and configure the maven-shade-plugin.

1.1.1. Enable the unnamed module Mode

To enable the unnamed module mode, add the RX_MICRO_BUILD_UNNAMED_MODULE compiler option to maven-compiler-plugin:

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>${maven-compiler-plugin.version}</version>
    <configuration>
        <release>11</release>
        <!-- ... -->
        <compilerArgs>
            <arg>-ARX_MICRO_BUILD_UNNAMED_MODULE=true</arg> (1)
        </compilerArgs>
    </configuration>
    <!-- ... -->
</plugin>
1 The RxMicro Annotation Processor will use the unnamed module mode for the current microservice project.

1.1.2. Configuration of maven-shade-plugin

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>${maven-shade-plugin.version}</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <createDependencyReducedPom>false</createDependencyReducedPom>
                <transformers>
                    <transformer implementation=
                                         "org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <manifestEntries>
                            <Main-Class>
                                io.rxmicro.examples.unnamed.module.uber.jar.HelloWorldMicroService (1)
                            </Main-Class>
                        </manifestEntries>
                    </transformer>
                </transformers>
                <filters>
                    <filter>
                        <artifact>*:*</artifact>
                        <excludes>
                            <exclude>ModuleInfo.*</exclude> (2)
                        </excludes>
                    </filter>
                </filters>
                <minimizeJar>true</minimizeJar> (3)
            </configuration>
        </execution>
    </executions>
</plugin>
1 It is necessary to specify a class with the main method, which will be used as an entry point into the microservice.
2 The resulting uber.jar must not contain the module virtual descriptor, as this descriptor is only needed at the compilation level. (More information about the virtual descriptor can be found in Section 1.2, “Module Configuration”.)
3 The resulting uber.jar can be reduced by excluding unused classes and packages.

The maven-shade-plugin plugin automatically removes all module-info.class from the resulting uber.jar, so all RxMicro modules will work in the unnamed module mode!

1.1.3. Rebuild Project

After pom.xml setting, it is necessary to rebuild the project:

mvn clean package

As a result, the uber.jar with all the necessary dependencies will be created:

:$ ls -lh

-rw-rw-r-- 1 nedis nedis 1,4M Mar 28 11:03 unnamed-module-uber-jar-1.0-SNAPSHOT.jar          (1)
-rw-rw-r-- 1 nedis nedis 9,8K Mar 28 11:03 original-unnamed-module-uber-jar-1.0-SNAPSHOT.jar (2)
1 The uber.jar size is equal to 1,4 MB.
2 The size of the original jar file is equal to 9.8 KB.

The uber.jar contains the source code of the microservice project, the code of the RxMicro and the Netty frameworks:

uber jar content
Figure 1. The uber.jar content for a simple microservice project

1.1.4. Run uber.jar

To run uber.jar it is necessary to provider jar file only:

> java -jar unnamed-module-uber-jar-1.0-SNAPSHOT.jar

2020-02-02 20:14:11.707 [INFO] io.rxmicro.rest.server.netty.internal.component.NettyServer :
Server started at 0.0.0.0:8080 using NETTY transport in 500 millis (1)
1 The Server started in …​ millis message format means that the RxMicro HTTP server has been successfully started.

To run a modularized micro service project the java requires more arguments:

java -p ./classes:lib -m examples.quick.start/io.rxmicro.examples.quick.start.HelloWorldMicroService

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

1.2. Module Configuration

Some RxMicro modules allow You to set the common configuration with RxMicro Annotations, which annotate the module-info.java module descriptor. But for the unnamed module, the module-info.java module descriptor does not exist!

In such cases a virtual descriptor must be created:

@Retention(CLASS)
@Target({})
public @interface ModuleInfo {
}

A virtual descriptor is a custom annotation that meets the following requirements:

  1. The name of the virtual descriptor is fixed and equal to: ModuleInfo.

  2. The annotation must be contained in the default package (unnamed package).

  3. The annotation should be available only during the compilation process: @Retention(CLASS).

  4. The annotation must contain an empty list of targets: @Target({}).

Since the virtual descriptor is available only during the compilation process, then during the uber.jar build process this annotation can be excluded from the resulting jar.

<filters>
    <filter>
        <artifact>*:*</artifact>
        <excludes>
            <exclude>ModuleInfo.*</exclude>
        </excludes>
    </filter>
</filters>

1.2.1. Configuration of the Code Generation Process for Rest Controller

The RxMicro framework provides the option to configure the code generation process for REST controllers. For this purpose, the @RestServerGeneratorConfig annotation should be used to annotate the module-info.java module descriptor.

For the unnamed module instead of the module-info.java module descriptor, use the ModuleInfo annotation:

@Retention(CLASS)
@Target({})
@RestServerGeneratorConfig(
        exchangeFormat = ServerExchangeFormatModule.JSON,
        generateRequestValidators = GenerateOption.DISABLED,
        generateResponseValidators = GenerateOption.AUTO_DETECT
)
public @interface ModuleInfo {
}

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

1.2.2. Configuration of the Code Generation Process for Rest Client

The RxMicro framework provides the option to configure the code generation process for REST clients. For this purpose, the @RestClientGeneratorConfig annotation should be used to annotate the module-info.java module descriptor.

For the unnamed module instead of the module-info.java module descriptor, use the ModuleInfo annotation:

@Retention(CLASS)
@Target({})
@RestClientGeneratorConfig(
        exchangeFormat = ClientExchangeFormatModule.JSON,
        generateRequestValidators = GenerateOption.AUTO_DETECT,
        requestValidationMode = RestClientGeneratorConfig.RequestValidationMode.RETURN_ERROR_SIGNAL,
        generateResponseValidators = GenerateOption.DISABLED
)
public @interface ModuleInfo {
}

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

1.2.3. REST-based Microservice Metadata Configuration

The RxMicro framework provides the option to configure the REST-based microservice documentation generation process, using the annotations. These annotations annotate the module-info.java module descriptor.

For the unnamed module instead of the module-info.java module descriptor, use the ModuleInfo annotation:

@Retention(CLASS)
@Target({})
@Title("Metadata Annotations")
@Description("*Project* _Description_")
@Author(
        name = "Richard Hendricks",
        email = "richard.hendricks@piedpiper.com"
)
@Author(
        name = "Bertram Gilfoyle",
        email = "bertram.gilfoyle@piedpiper.com"
)
@Author(
        name = "Dinesh Chugtai",
        email = "dinesh.chugtai@piedpiper.com"
)
@BaseEndpoint("https://api.rxmicro.io")
@License(
        name = "Apache License 2.0",
        url = "https://github.com/rxmicro/rxmicro/blob/master/LICENSE"
)
@DocumentationDefinition(
        introduction = @IntroductionDefinition(
                sectionOrder = {
                        IntroductionDefinition.Section.BASE_ENDPOINT,
                        IntroductionDefinition.Section.LICENSES
                }
        ),
        resource = @ResourceDefinition(
                withInternalErrorResponse = false
        ),
        withGeneratedDate = false
)
public @interface ModuleInfo {
}

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

2. Using GraalVM to Build a Native Image

GraalVM is a universal virtual machine for running applications written in different languages.

GraalVM contains a Native Image Tool, that allows You to ahead-of-time compile Java code to a standalone executable, called a native image.

The GraalVM Native Image Tool can be used to build a native image of any RxMicro microservice project. Before build a native image it is necessary to setup a GraalVM developer environment.

2.1. Setup a GraalVM

GraalVM is distributed as Community Edition and Enterprise Edition.

Current guide describes a use of the GraalVM Community Edition (GraalVM CE)!

Setup of the GraalVM CE contains a few simple steps:

  1. Make a GraalVM Home Directory.

  2. Select a GraalVM CE Distribution for Your Platform.

  3. Download the GraalVM CE Distribution.

  4. Unzip the GraalVM CE Distribution.

  5. Set a GRAALVM_HOME Environment Variable.

  6. Install a Native Image Module.

  7. Add the Native Image Tool to PATH.

2.1.1. Make a GraalVM Home Directory

Make a directory, that will contain a GraalVM distribution:

mkdir ~/GraalVM

2.1.2. Select a GraalVM CE Distribution for Your Platform

Visit the github release page https://github.com/graalvm/graalvm-ce-builds/releases and select a distribution for Your platform.

Please select a GraalVM CE based on Java 11!

A GraalVM CE based on Java 8 is not supported by the RxMicro framework!

2.1.3. Download the GraalVM CE Distribution

Download the selected GraalVM CE distribution onto the GraalVM home directory:

wget -P ~/GraalVM https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-20.0.0/graalvm-ce-java11-linux-amd64-20.0.0.tar.gz

2.1.4. Unzip the GraalVM CE Distribution

Unzip the GraalVM CE distribution onto the GraalVM home directory:

tar --strip-components=1 -vzxf ~/GraalVM/graalvm-ce-java11-linux-amd64-20.0.0.tar.gz -C ~/GraalVM
rm ~/GraalVM/graalvm-ce-java11-linux-amd64-20.0.0.tar.gz

2.1.5. Set a GRAALVM_HOME Environment Variable

Set a GRAALVM_HOME environment variable:

export GRAALVM_HOME=~/GraalVM

and verify the installation:

$GRAALVM_HOME/bin/java -version

openjdk version "11.0.6" 2020-01-14
OpenJDK Runtime Environment GraalVM CE 20.0.0 (build 11.0.6+9-jvmci-20.0-b02)
OpenJDK 64-Bit Server VM GraalVM CE 20.0.0 (build 11.0.6+9-jvmci-20.0-b02, mixed mode, sharing)

If You would use the GraalVM CE only, You can add the $GRAALVM_HOME/bin path to the PATH variable!

If You use any other version of JDK, add only the separate utils to PATH variable. For example only gu or native-image!

2.1.6. Install a Native Image Module

Starting from GraalVM 19.0, Native Image was extracted from the base distribution. So the Native Image Tool must be installed to GraalVM using a GraalVM Updater utility:

$GRAALVM_HOME/bin/gu install native-image

Downloading: Component catalog from www.graalvm.org
Processing Component: Native Image
Downloading: Component native-image: Native Image  from github.com
Installing new component: Native Image (org.graalvm.native-image, version 20.0.0)

To verify the installation show a version of the native Native Image Tool:

$GRAALVM_HOME/bin/native-image --version

GraalVM Version 20.0.0 CE

2.1.7. Add the Native Image Tool to PATH

In order not to indicate the folder where the Native Image Tool was installed, add the Native Image Tool to the PATH variable:

ln -s $GRAALVM_HOME/bin/native-image ~/bin

After that logout and login to the system again. The Native Image Tool must be available at the terminal:

native-image --version

GraalVM Version 20.0.0 CE

2.2. RxMicro Project Configuration

The first release of GraalVM based on Java 11 is 19.3.0 (2019-11-19). But unfortunately the GraalVM Native Image Tool does not support JPMS yet.

Thus we need to use the unnamed module mode for RxMicro project.

2.2.1. Setup pom.xml

2.2.1.1. Using RX_MICRO_BUILD_UNNAMED_MODULE Option

To enable the unnamed module mode, it is necessary to add RX_MICRO_BUILD_UNNAMED_MODULE option to the maven-compiler-plugin:

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <!-- ... -->
        <compilerArgs>
            <arg>-ARX_MICRO_BUILD_UNNAMED_MODULE=true</arg>
        </compilerArgs>
    </configuration>
    <!-- ... -->
</plugin>
2.2.1.2. Maven-shade-plugin Configuration

To build a native image we need a uber jar. For that it is necessary to add maven-shade-plugin:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>${maven-shade-plugin.version}</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <createDependencyReducedPom>false</createDependencyReducedPom>
                <transformers>
                    <transformer implementation=
                        "org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <manifestEntries>
                            <Main-Class>
                        io.rxmicro.examples.graalvm.nativeimage.quick.start.HelloWorldMicroService
                            </Main-Class>
                        </manifestEntries>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>
2.2.1.3. Exec-maven-plugin Configuration

To build a native image from maven it is necessary to use exec-maven-plugin:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>${exec-maven-plugin.version}</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>exec</goal>
            </goals>
            <configuration>
                <executable>native-image</executable> (1)
                <arguments>
                    <argument>--verbose</argument>
                    <argument>-jar</argument>
                    <argument>--install-exit-handlers</argument> (2)
                    <argument>${project.build.directory}/${project.build.finalName}.jar</argument>
                </arguments>
            </configuration>
        </execution>
    </executions>
</plugin>
1 An executable will be the native-image tool with specified arguments.
2 This option adds the exit handler for native image.

The Native Image Tool (native-image) must be available via PATH variable!

2.2.2. Microservice Source Code

A microservice source code contains two classes only:

final class Response {

    final String message;

    Response(final String message) {
        this.message = requireNonNull(message);
    }
}
public final class HelloWorldMicroService {

    @GET("/")
    CompletableFuture<Response> sayHelloWorld() {
        return CompletableFuture.supplyAsync(() ->
                new Response("Hello World!"));
    }

    public static void main(final String[] args) {
        new Configs.Builder()
                .withContainerConfigSources() (1)
                .build();
        RxMicroRestServer.startRestServer(HelloWorldMicroService.class);
    }
}
1 Docker based configuration activates the configuration via annotations and environment variables only.

2.2.3. Classpath Resources

A RxMicro logger is configured using jul.properties classpath resource.

jul.properties contains the following content:

io.rxmicro.rest.server.level=TRACE

A native image is configured using META-INF/native-image classpath directory with two files:

  • native-image.properties

  • resource-config.json

native-image.properties contains the following content:

Args = --no-fallback \
       --allow-incomplete-classpath \
       -H:ResourceConfigurationResources=${.}/resource-config.json

resource-config.json contains the following content:

{
  "resources":[
    {"pattern":"\\Qjul.properties\\E"}
  ],
  "bundles":[]
}

2.3. Creation of the Native Image

To build a native image run:

mvn clean package

The built native image executable will be available at the project home directory:

ls -lh

drwxrwxr-x 5 nedis nedis 4,0K Mar 25 20:44 src
drwxrwxr-x 8 nedis nedis 4,0K Mar 28 21:30 target
-rw-rw-r-- 1 nedis nedis 8,8K Mar 28 21:02 pom.xml
-rwxrwxr-x 1 nedis nedis  17M Mar 28 21:32 HelloWorldMicroService (1)
1 The built native image executable

2.4. Verification of the Native Image

To verify the built native image run:

./HelloWorldMicroService

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

Monitoring

Home

Testing