1. The javac Command

The javac command reads source files that contain module, package and type declarations written in the Java programming language, and compiles them into class files that run on the Java Virtual Machine.

// Hello.java
public class Hello {
   public static void main(String[] args) {
      System.out.println("Hello Java!");
   }
}
// Complile with `javac`
$ javac Hello.java
$ java Hello
Hello Java!

// javac -d and java -cp
$ javac Hello.java -d target/classes/
$ java -cp target/classes/ Hello
Hello Java!

2. The java Command

The java command starts a Java application. It does this by starting the Java Virtual Machine (JVM), loading the specified class, and calling that class’s main() method. The method must be declared public and static, it must not return any value, and it must accept a String array as a parameter. The method declaration has the following form:

public static void main(String[] args)

In source-file mode, the java command can launch a class declared in a source file.

You can use the JDK_JAVA_OPTIONS launcher environment variable to prepend its content to the actual command line of the java launcher.

By default, the first argument that isn’t an option of the java command is the fully qualified name of the class to be called. If -jar is specified, then its argument is the name of the JAR file containing class and resource files for the application. The startup class must be indicated by the Main-Class manifest header in its manifest file.

Arguments after the class file name or the JAR file name are passed to the main() method.

  • To launch a single source-file program

    • Synopsis

      java [options] source-file [args ...]
    • java Hello.java

      $ java Hello.java
      Hello Java!
  • To launch a class file

    • Synopsis

      java [options] mainclass [args ...]
    • java Hello

      $ javac Hello.java
      $ java Hello
      Hello Java!
  • To launch the main class in a JAR file

    java [options] -jar jarfile [args ...]

    The jarfile argument is the name of a JAR file with a manifest that contains a line in the form Main-Class:classname that defines the class with the public static void main(String[] args) method that serves as your application’s starting point.

    When you use -jar, the specified JAR file is the source of all user classes, and other class path settings are ignored.

    The jar command
    jar [OPTION ...] [ [--release VERSION] [-C dir] files] ...
    $ jar -cf foo.jar Hello.class
    $ java -cp foo.jar Hello
    Hello Java!
    $ java -jar foo.jar
    no main manifest attribute, in foo.jar
    
    $ jar --create --file buz.jar --main-class Hello Hello.class
    $ java -jar buz.jar
    Hello Java!
    
    $ jar xf buz.jar META-INF/
    $ cat META-INF/MANIFEST.MF
    Manifest-Version: 1.0
    Created-By: 11.0.12 (Debian)
    Main-Class: Hello
    

2.1. Overview of Java Options

The java command supports a wide range of options in the following categories:

  • Standard Options:

    Options guaranteed to be supported by all implementations of the Java Virtual Machine (JVM).

    They’re used for common actions, such as checking the version of the JRE, setting the class path, enabling verbose output, and so on.

  • Extra Options:

    General purpose options that are specific to the Java HotSpot Virtual Machine.

    They aren’t guaranteed to be supported by all JVM implementations, and are subject to change. These options start with -X.

  • Advanced Options

    The advanced options aren’t recommended for casual use. These are developer options used for tuning specific areas of the Java HotSpot Virtual Machine operation that often have specific system requirements and may require privileged access to system configuration parameters.

    These options aren’t guaranteed to be supported by all JVM implementations and are subject to change. These options start with -XX.

    • Runtime Options:

      Control the runtime behavior of the Java HotSpot VM.

    • JIT Compiler Options:

      Control the dynamic just-in-time (JIT) compilation performed by the Java HotSpot VM.

    • Serviceability Options:

      Enable gathering system information and performing extensive debugging.

    • Garbage Collection Options:

      Control how garbage collection (GC) is performed by the Java HotSpot

    Boolean -XX options are enabled using the plus sign (-XX:+OptionName) and disabled using the minus sign (-XX:-OptionName).

For options that require an argument, the argument may be

  • separated from the option name by a space, a colon (:), or an equal sign (=),

  • or the argument may directly follow the option (the exact syntax differs for each option).

If you’re expected to specify the size in bytes, then you can use no suffix, or use the suffix k or K for kilobytes (KB), m or M for megabytes (MB), or g or G for gigabytes (GB).

For example, to set the size to 8 GB, you can specify either 8g, 8192m, 8388608k, or 8589934592 as the argument.

If you are expected to specify the percentage, then use a number from 0 to 1.

For example, specify 0.25 for 25%.

2.2. Standard Options for Java

These are the most commonly used options supported by all implementations of the JVM.

To specify an argument for a long option, you can use either --name=value or --name value.

  • --class-path classpath, -classpath classpath, or -cp classpath

    A semicolon (;) separated list of directories, JAR archives, and ZIP archives to search for class files. Specifying classpath overrides any setting of the CLASSPATH environment variable.

    If the class path option isn’t used and classpath isn’t set, then the user class path consists of the current directory (.).

    As a special convenience, a class path element that contains a base name of an asterisk (*) is considered equivalent to specifying a list of all the files in the directory with the extension .jar or .JAR .

    A Java program can’t tell the difference between the two invocations.

    For example, if the directory mydir contains a.jar and b.JAR, then the class path element mydir/* is expanded to A.jar:b.JAR, except that the order of JAR files is unspecified. All .jar files in the specified directory, even hidden ones, are included in the list.

    A class path entry consisting of an asterisk (*) expands to a list of all the jar files in the current directory.

    The CLASSPATH environment variable, where defined, is similarly expanded.

    Any class path wildcard expansion that occurs before the Java VM is started.

    Java programs never see wildcards that aren’t expanded except by querying the environment, such as by calling System.getenv("CLASSPATH").

  • --list-modules

    Lists the observable modules and then exits.

  • -d module_name or --describe-module module_name

    Describes a specified module and then exits.

  • --dry-run

    Creates the VM but doesn’t execute the main method.

    This --dry-run option might be useful for validating the command-line options such as the module system configuration.

  • --validate-modules

    Validates all modules and exit. This option is helpful for finding conflicts and other errors with modules on the module path.

  • -Dproperty=value

    Sets a system property value.

    The property variable is a string with no spaces that represents the name of the property. The value variable is a string that represents the value of the property.

    If value is a string with spaces, then enclose it in quotation marks (for example -Dfoo="foo bar").

  • -verbose:class

    Displays information about each loaded class.

  • -verbose:gc

    Displays information about each garbage collection (GC) event.

  • -verbose:jni

    Displays information about the use of native methods and other Java Native Interface (JNI) activity.

  • -verbose:module

    Displays information about the modules in use.

  • -X

    Prints the help on extra options to the error stream.

2.3. Extra Options for Java

The following java options are general purpose options that are specific to the Java HotSpot Virtual Machine.

  • -Xlog:option

    Configure or enable logging with the Java Virtual Machine (JVM) unified logging framework.

  • -Xinternalversion

    Displays more detailed JVM version information than the -version option, and then exits.

  • -Xmn size

    Sets the initial and maximum size (in bytes) of the heap for the young generation (nursery) in the generational collectors.

    Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, or g or G to indicate gigabytes.

    The young generation region of the heap is used for new objects.

    • GC is performed in this region more often than in other regions.

    • If the size for the young generation is too small, then a lot of minor garbage collections are performed.

    • If the size is too large, then only full garbage collections are performed, which can take a long time to complete.

    • It is recommended that you do not set the size for the young generation for the G1 collector, and keep the size for the young generation greater than 25% and less than 50% of the overall heap size for other collectors.

    The following examples show how to set the initial and maximum size of young generation to 256 MB using various units:

    -Xmn256m
    -Xmn262144k
    -Xmn268435456

    Instead of the -Xmn option to set both the initial and maximum size of the heap for the young generation, you can use -XX:NewSize to set the initial size and -XX:MaxNewSize to set the maximum size.

  • -Xms size

    Sets the minimum and initial size (in bytes) of the heap.

    This value must be a multiple of 1024 and greater than 1 MB.

    Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, g or G to indicate gigabytes.

    The following examples show how to set the size of allocated memory to 6 MB using various units:

    -Xms6291456
    -Xms6144k
    -Xms6m

    Instead of the -Xms option to set both the minimum and initial size of the heap, you can use -XX:MinHeapSize to set the minimum size and -XX:InitialHeapSize to set the initial size.

    If you don’t set this option, the initial size is set as the sum of the sizes allocated for the old generation and the young generation.

    The initial size of the heap for the young generation can be set using the -Xmn option or the -XX:NewSize option.

  • -Xmx size

    Specifies the maximum size (in bytes) of the heap.

    This value must be a multiple of 1024 and greater than 2 MB.

    Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, or g or G to indicate gigabytes.

    The default value is chosen at runtime based on system configuration.

    For server deployments, -Xms and -Xmx are often set to the same value.

    The following examples show how to set the maximum allowed size of allocated memory to 80 MB using various units:

    -Xmx83886080
    -Xmx81920k
    -Xmx80m

    The -Xmx option is equivalent to -XX:MaxHeapSize.

  • -XshowSettings

    Shows all settings and then continues.

  • -XshowSettings:category

    Shows settings and continues.

    Possible category arguments for this option include the following:

    • all

      Shows all categories of settings. This is the default value.

    • locale

      Shows settings related to locale.

    • properties

      Shows settings related to system properties.

    • vm

      Shows the settings of the JVM.

    • system

      Linux: Shows host system or container configuration and continues.

  • -Xss size

    Sets the thread stack size (in bytes).

    Append the letter k or K to indicate KB, m or M to indicate MB, or g or G to indicate GB.

    The default value depends on the platform:

    • Linux/x64 (64-bit): 1024 KB

    • macOS (64-bit): 1024 KB

    • Windows: The default value depends on virtual memory

    The following examples set the thread stack size to 1024 KB in different units:

    -Xss1m
    -Xss1024k
    -Xss1048576

    This option is similar to -XX:ThreadStackSize.

  • --source version

    Sets the version of the source in source-file mode.

2.4. Advanced Options for Java

These java options can be used to enable other advanced options.

  • -XX:+UnlockDiagnosticVMOptions

    Unlocks the options intended for diagnosing the JVM. By default, this option is disabled and diagnostic options aren’t available.

    Command line options that are enabled with the use of this option are not supported. If you encounter issues while using any of these options, it is very likely that you will be required to reproduce the problem without using any of these unsupported options before Oracle Support can assist with an investigation. It is also possible that any of these options may be removed or their behavior changed without any warning.

  • -XX:+UnlockExperimentalVMOptions

    Unlocks the options that provide experimental features in the JVM. By default, this option is disabled and experimental features aren’t available.

  • -XX:+PrintFlagsInitial

    Print all the default values of all XX flags.

  • -XX:+PrintFlagsFinal

    Print all the current values to all XX flags.

2.5. Advanced Runtime Options for Java

These java options control the runtime behavior of the Java HotSpot VM.

  • -XX:ActiveProcessorCount=x

    Overrides the number of CPUs that the VM will use to calculate the size of thread pools it will use for various operations such as Garbage Collection and ForkJoinPool.

    The VM normally determines the number of available processors from the operating system.

    This flag can be useful for partitioning CPU resources when running multiple Java processes in docker containers.

    This flag is honored even if -XX:-UseContainerSupport is not enabled.

  • -XX:MaxDirectMemorySize=size

    Sets the maximum total size (in bytes) of the java.nio package, direct-buffer allocations.

    Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, or g or G to indicate gigabytes.

    By default, the size is set to 0, meaning that the JVM chooses the size for NIO direct-buffer allocations automatically.

    The following examples illustrate how to set the NIO size to 1024 KB in different units:

    -XX:MaxDirectMemorySize=1m
    -XX:MaxDirectMemorySize=1024k
    -XX:MaxDirectMemorySize=1048576
  • -XX:NativeMemoryTracking=mode

    Specifies the mode for tracking JVM native memory usage.

    Possible mode arguments for this option include the following:

    • off

      Instructs not to track JVM native memory usage.

      This is the default behavior if you don’t specify the -XX:NativeMemoryTracking option.

    • summary

      Tracks memory usage only by JVM subsystems, such as Java heap, class, code, and thread.

    • detail

      In addition to tracking memory usage by JVM subsystems, track memory usage by individual CallSite, individual virtual memory region and its committed regions.

  • -XX:OnError=string

    Sets a custom command or a series of semicolon-separated commands to run when an irrecoverable error occurs.

    If the string contains spaces, then it must be enclosed in quotation marks.

    • Linux and macOS:

      The following example shows how the -XX:OnError option can be used to run the gcore command to create a core image, and start the gdb debugger to attach to the process in case of an irrecoverable error (the %p designates the current process identifier):

      -XX:OnError="gcore %p;gdb -p %p"
    • Windows:

      The following example shows how the -XX:OnError option can be used to run the userdump.exe utility to obtain a crash dump in case of an irrecoverable error (the %p designates the current process identifier).

      This example assumes that the path to the userdump.exe utility is specified in the PATH environment variable:

      -XX:OnError="userdump.exe %p"
  • -XX:OnOutOfMemoryError=string

    Sets a custom command or a series of semicolon-separated commands to run when an OutOfMemoryError exception is first thrown.

    If the string contains spaces, then it must be enclosed in quotation marks.

    For an example of a command string, see the description of the -XX:OnError option.

  • -XX:+PrintCommandLineFlags

    Enables printing of ergonomically selected JVM flags that appeared on the command line.

    It can be useful to know the ergonomic values set by the JVM, such as the heap space size and the selected garbage collector.

    By default, this option is disabled and flags aren’t printed.

  • -XX:+PrintNMTStatistics

    Enables printing of collected native memory tracking data at JVM exit when native memory tracking is enabled (see -XX:NativeMemoryTracking).

    By default, this option is disabled and native memory tracking data isn’t printed.

  • -XX:ThreadStackSize=size

    Sets the Java thread stack size (in kilobytes).

    Use of a scaling suffix, such as k, results in the scaling of the kilobytes value so that -XX:ThreadStackSize=1k sets the Java thread stack size to 1024*1024 bytes or 1 megabyte.

    The default value depends on the platform:

    • Linux/x64 (64-bit): 1024 KB

    • macOS (64-bit): 1024 KB

    • Windows: The default value depends on virtual memory

    The following examples show how to set the thread stack size to 1 megabyte in different units:

    -XX:ThreadStackSize=1k
    -XX:ThreadStackSize=1024

    This option is similar to -Xss.

  • -XX:-UseContainerSupport

    The VM now provides automatic container detection support, which allows the VM to determine the amount of memory and number of processors that are available to a Java process running in docker containers.

    It uses this information to allocate system resources.

    This support is only available on Linux x64 platforms.

    If supported, the default for this flag is true, and container support is enabled by default.

    It can be disabled with -XX:-UseContainerSupport.

    Unified Logging is available to help to diagnose issues related to this support.

    Use -Xlog:os+container=trace for maximum logging of container information.

2.6. Advanced JIT Compiler Options for java

These java options control the dynamic just-in-time (JIT) compilation performed by the Java HotSpot VM.

  • -XX:InitialCodeCacheSize=size

    Sets the initial code cache size (in bytes).

    Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, or g or G to indicate gigabytes.

    The default value depends on the platform.

    The initial code cache size shouldn’t be less than the system’s minimal memory page size.

    The following example shows how to set the initial code cache size to 32 KB:

    -XX:InitialCodeCacheSize=32k
  • -XX:ReservedCodeCacheSize=size

    Sets the maximum code cache size (in bytes) for JIT-compiled code.

    Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, or g or G to indicate gigabytes.

    The default maximum code cache size is 240 MB; if you disable tiered compilation with the option -XX:-TieredCompilation, then the default size is 48 MB.

    This option has a limit of 2 GB; otherwise, an error is generated.

    The maximum code cache size shouldn’t be less than the initial code cache size; see the option -XX:InitialCodeCacheSize.

  • -XX:-TieredCompilation

    Disables the use of tiered compilation.

    By default, this option is enabled.

2.7. Advanced Garbage Collection Options for Java

These java options control how garbage collection (GC) is performed by the Java HotSpot VM.

  • -XX:ConcGCThreads=threads

    Sets the number of threads used for concurrent GC.

    Sets threads to approximately 1/4 of the number of parallel garbage collection threads.

    The default value depends on the number of CPUs available to the JVM.

    For example, to set the number of threads for concurrent GC to 2, specify the following option:

    -XX:ConcGCThreads=2
  • -XX:+DisableExplicitGC

    Enables the option that disables processing of calls to the System.gc() method.

    This option is disabled by default, meaning that calls to System.gc() are processed.

    If processing of calls to System.gc() is disabled, then the JVM still performs GC when necessary.

  • -XX:+ExplicitGCInvokesConcurrent

    Enables invoking of concurrent GC by using the System.gc() request.

    This option is disabled by default and can be enabled only with the -XX:+UseG1GC option.

  • -XX:InitialHeapSize=size

    Sets the initial size (in bytes) of the memory allocation pool.

    This value must be either 0, or a multiple of 1024 and greater than 1 MB.

    Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, or g or G to indicate gigabytes.

    The default value is selected at run time based on the system configuration.

    The following examples show how to set the size of allocated memory to 6 MB using various units:

    -XX:InitialHeapSize=6291456
    -XX:InitialHeapSize=6144k
    -XX:InitialHeapSize=6m

    If you set this option to 0, then the initial size is set as the sum of the sizes allocated for the old generation and the young generation.

    The size of the heap for the young generation can be set using the -XX:NewSize option.

  • -XX:InitialRAMPercentage=percent

    Sets the initial amount of memory that the JVM will use for the Java heap before applying ergonomics heuristics as a percentage of the maximum amount determined as described in the -XX:MaxRAM option.

    The default value is 1.5625 percent.

    The following example shows how to set the percentage of the initial amount of memory used for the Java heap:

    -XX:InitialRAMPercentage=5
  • -XX:MaxGCPauseMillis=time

    Sets a target for the maximum GC pause time (in milliseconds).

    This is a soft goal, and the JVM will make its best effort to achieve it.

    The specified value doesn’t adapt to your heap size.

    By default, for G1 the maximum pause time target is 200 milliseconds.

    The other generational collectors do not use a pause time goal by default.

    The following example shows how to set the maximum target pause time to 500 ms:

    -XX:MaxGCPauseMillis=500
  • -XX:MaxHeapSize=size

    Sets the maximum size (in byes) of the memory allocation pool.

    This value must be a multiple of 1024 and greater than 2 MB.

    Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, or g or G to indicate gigabytes.

    The default value is selected at run time based on the system configuration.

    For server deployments, the options -XX:InitialHeapSize and -XX:MaxHeapSize are often set to the same value.

    The following examples show how to set the maximum allowed size of allocated memory to 80 MB using various units:

    -XX:MaxHeapSize=83886080
    -XX:MaxHeapSize=81920k
    -XX:MaxHeapSize=80m

    The -XX:MaxHeapSize option is equivalent to -Xmx.

  • -XX:MaxMetaspaceSize=size

    Sets the maximum amount of native memory that can be allocated for class metadata.

    By default, the size isn’t limited.

    The amount of metadata for an application depends on the application itself, other running applications, and the amount of memory available on the system.

    The following example shows how to set the maximum class metadata size to 256 MB:

    -XX:MaxMetaspaceSize=256m
  • -XX:MaxNewSize=size

    Sets the maximum size (in bytes) of the heap for the young generation (nursery).

    The default value is set ergonomically.

  • -XX:MaxRAM=size

    Sets the maximum amount of memory that the JVM may use for the Java heap before applying ergonomics heuristics.

    The default value is the maximum amount of available memory to the JVM process or 128 GB, whichever is lower.

    The maximum amount of available memory to the JVM process is the minimum of the machine’s physical memory and any constraints set by the environment (e.g. container).

    Specifying this option disables automatic use of compressed oops if the combined result of this and other options influencing the maximum amount of memory is larger than the range of memory addressable by compressed oops.

    The following example shows how to set the maximum amount of available memory for sizing the Java heap to 2 GB:

    -XX:MaxRAM=2G
  • -XX:MaxRAMPercentage=percent

    Sets the maximum amount of memory that the JVM may use for the Java heap before applying ergonomics heuristics as a percentage of the maximum amount determined as described in the -XX:MaxRAM option.

    The default value is 25 percent.

    Specifying this option disables automatic use of compressed oops if the combined result of this and other options influencing the maximum amount of memory is larger than the range of memory addressable by compressed oops. See -XX:UseCompressedOops for further information about compressed oops.

    The following example shows how to set the percentage of the maximum amount of memory used for the Java heap:

    -XX:MaxRAMPercentage=75
  • -XX:MinRAMPercentage=percent

    Sets the maximum amount of memory that the JVM may use for the Java heap before applying ergonomics heuristics as a percentage of the maximum amount determined as described in the -XX:MaxRAM option for small heaps.

    A small heap is a heap of approximately 125 MB.

    The default value is 50 percent.

    The following example shows how to set the percentage of the maximum amount of memory used for the Java heap for small heaps:

    -XX:MinRAMPercentage=75
  • -XX:MetaspaceSize=size

    Sets the size of the allocated class metadata space that triggers a garbage collection the first time it’s exceeded. This threshold for a garbage collection is increased or decreased depending on the amount of metadata used. The default size depends on the platform.

  • -XX:MinHeapSize=size

    Sets the minimum size (in bytes) of the memory allocation pool. This value must be either 0, or a multiple of 1024 and greater than 1 MB. Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, or g or G to indicate gigabytes. The default value is selected at run time based on the system configuration.

    The following examples show how to set the mimimum size of allocated memory to 6 MB using various units:

    -XX:MinHeapSize=6291456
    -XX:MinHeapSize=6144k
    -XX:MinHeapSize=6m

    If you set this option to 0, then the minimum size is set to the same value as the initial size.

  • -XX:NewSize=size

    Sets the initial size (in bytes) of the heap for the young generation (nursery).

    Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, or g or G to indicate gigabytes.

    The young generation region of the heap is used for new objects.

    • GC is performed in this region more often than in other regions.

    • If the size for the young generation is too low, then a large number of minor GCs are performed.

    • If the size is too high, then only full GCs are performed, which can take a long time to complete.

    • It is recommended that you keep the size for the young generation greater than 25% and less than 50% of the overall heap size.

    The following examples show how to set the initial size of the young generation to 256 MB using various units:

    -XX:NewSize=256m
    -XX:NewSize=262144k
    -XX:NewSize=268435456

    The -XX:NewSize option is equivalent to -Xmn.

  • -XX:+UseG1GC

    Enables the use of the garbage-first (G1) garbage collector.

    It’s a server-style garbage collector, targeted for multiprocessor machines with a large amount of RAM.

    This option meets GC pause time goals with high probability, while maintaining good throughput.

    The G1 collector is recommended for applications requiring large heaps (sizes of around 6 GB or larger) with limited GC latency requirements (a stable and predictable pause time below 0.5 seconds).

    By default, this option is enabled and G1 is used as the default garbage collector.

2.8. Removed Java Options

These java options have been removed in JDK 17 and using them results in an error of:

Unrecognized VM option option-name
  • -XX:MaxPermSize=size

    Sets the maximum permanent generation space size (in bytes).

    This option was deprecated in JDK 8 and superseded by the -XX:MaxMetaspaceSize option.

  • -XX:PermSize=size

    Sets the space (in bytes) allocated to the permanent generation that triggers a garbage collection if it’s exceeded.

    This option was deprecated in JDK 8 and superseded by the -XX:MetaspaceSize option.

3. Maven

Apache Maven is a software project management and comprehension tool based on the concept of a project object model (POM).

3.1. The Build Lifecycle

Maven is based around the central concept of a build lifecycle.

There are three built-in build lifecycles:

  • The default lifecycle handles your project deployment,

  • the clean lifecycle handles project cleaning,

  • while the site lifecycle handles the creation of your project’s web site.

A Build Lifecycle is Made Up of Phases

Each of these build lifecycles is defined by a different list of build phases, wherein a build phase represents a stage in the lifecycle.

For example, the default lifecycle comprises of the following phases:

  • validate

    • validate the project is correct and all necessary information is available

  • compile

    • compile the source code of the project

  • test

    • test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployed

  • package

    • take the compiled code and package it in its distributable format, such as a JAR.

  • verify

    • run any checks on results of integration tests to ensure quality criteria are met

  • install

    • install the package into the local repository, for use as a dependency in other projects locally

  • deploy

    • done in the build environment, copies the final package to the remote repository for sharing with other developers and projects.

These lifecycle phases (plus the other lifecycle phases not shown here) are executed sequentially to complete the default lifecycle.

Given the lifecycle phases above, this means that when the default lifecycle is used, Maven will first

  1. validate the project,

  2. then will try to compile the sources,

  3. run those against the tests,

  4. package the binaries (e.g. jar),

  5. run integration tests against that package,

  6. verify the integration tests,

  7. install the verified package to the local repository,

  8. then deploy the installed package to a remote repository.

A Build Phase is Made Up of Plugin Goals

However, even though a build phase is responsible for a specific step in the build lifecycle, the manner in which it carries out those responsibilities may vary. And this is done by declaring the plugin goals bound to those build phases.

A plugin goal represents a specific task (finer than a build phase) which contributes to the building and managing of a project. It may be bound to zero or more build phases.

A goal not bound to any build phase could be executed outside of the build lifecycle by direct invocation.

The order of execution depends on the order in which the goal(s) and the build phase(s) are invoked.

For example, consider the command below. The clean and package arguments are build phases, while the dependency:copy-dependencies is a goal (of a plugin).

mvn clean dependency:copy-dependencies package

If this were to be executed, the clean phase will be executed first (meaning it will run all preceding phases of the clean lifecycle, plus the clean phase itself), and then the dependency:copy-dependencies goal, before finally executing the package phase (and all its preceding build phases of the default lifecycle).

Moreover, if a goal is bound to one or more build phases, that goal will be called in all those phases.

Furthermore, a build phase can also have zero or more goals bound to it.

If a build phase has no goals bound to it, that build phase will not execute.

But if it has one or more goals bound to it, it will execute all those goals.

Setting Up Your Project to Use the Build Lifecycle

The build lifecycle is simple enough to use, but when you are constructing a Maven build for a project, how do you go about assigning tasks to each of those build phases?

  • Packaging

    Each packaging contains a list of goals to bind to a particular phase.

    Some of the valid packaging values are jar, war, ear and pom.

    If no packaging value has been specified, it will default to jar.

  • Plugins

    Plugins are artifacts that provide goals to Maven.

    A plugin may have one or more goals wherein each goal represents a capability of that plugin.

    For example, the Compiler plugin has two goals: compile and testCompile.

    The former compiles the source code of your main code, while the latter compiles the source code of your test code.

    The goals that are configured will be added to the goals already bound to the lifecycle from the packaging selected.

    If more than one goal is bound to a particular phase, the order used is that those from the packaging are executed first, followed by those configured in the POM.

    Note that you can use the <executions> element to gain more control over the order of particular goals.

3.2. Standard Directory Layout

Having a common directory layout allows users familiar with one Maven project to immediately feel at home in another Maven project. The advantages are analogous to adopting a site-wide look-and-feel.

The next section documents the directory layout expected by Maven and the directory layout created by Maven. Try to conform to this structure as much as possible. However, if you can’t, these settings can be overridden via the project descriptor.

src/main/java

Application/Library sources

src/main/resources

Application/Library resources

src/main/filters

Resource filter files

src/main/webapp

Web application sources

src/test/java

Test sources

src/test/resources

Test resources

src/test/filters

Test resource filter files

src/it

Integration Tests (primarily for plugins)

src/assembly

Assembly descriptors

src/site

Site

LICENSE.txt

Project’s license

NOTICE.txt

Notices and attributions required by libraries that the project depends on

README.txt

Project’s readme

3.3. Configuring Plug-ins

In Maven, there are two kinds of plugins, build and reporting:

  • Build plugins are executed during the build and configured in the <build/> element.

  • Reporting plugins are executed during the site generation and configured in the <reporting/> element.

All plugins should have minimal required information: groupId, artifactId and version.

Maven plugins (build and reporting) are configured by specifying a <configuration> element where the child elements of the <configuration> element are mapped to fields, or setters, inside your Mojo.

Remember that a plug-in consists of one or more Mojos where a Mojo maps to a goal.

For example, you have a Mojo that performs a query against a particular URL, with a specified timeout and list of options. The Mojo might look like the following:

/**
 * @goal query
 */
public class MyQueryMojo extends AbstractMojo {
    @Parameter(property = "query.url", required = true)
    private String url;

    @Parameter(property = "timeout", required = false, defaultValue = "50")
    private int timeout;

    @Parameter(property = "options")
    private String[] options;

    public void execute() throws MojoExecutionException {
        // ...
    }
}

To configure the Mojo from your POM with the desired URL, timeout and options you might have something like the following:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-myquery-plugin</artifactId>
        <version>1.0</version>
        <configuration>
          <url>http://www.foobar.com/query</url>
          <timeout>10</timeout>
          <options>
            <option>one</option>
            <option>two</option>
            <option>three</option>
          </options>
        </configuration>
      </plugin>
    </plugins>
  </build>
    ...
</project>

The elements in the configuration match the names of the fields in the Mojo.

For Mojos that are intended to be executed directly from the CLI, their parameters usually provide a means to be configured via system properties instead of a <configuration> section in the POM.

The plugin documentation for those parameters will list an expression that denotes the system properties for the configuration.

In the Mojo above, the parameter url is associated with the expression ${query.url}, meaning its value can be specified by the system property query.url as shown below:

mvn myquery:query -Dquery.url=http://maven.apache.org

Help Goal

Most Maven plugins have a help goal that prints a description of the plugin and its parameters and types. For instance, to see help for the javadoc goal, type:

mvn javadoc:help -Ddetail -Dgoal=javadoc

Configuring Parameters

  • Mapping Simple Objects

    <configuration>
      <myString>a string</myString>
      <myBoolean>true</myBoolean>
      <myInteger>10</myInteger>
      <myDouble>1.0</myDouble>
      <myFile>c:\temp</myFile>
      <myURL>http://maven.apache.org</myURL>
    </configuration>
  • Mapping Complex Objects

    <configuration>
      <person>
        <firstName>Jason</firstName>
        <lastName>van Zyl</lastName>
      </person>
    </configuration>
    <configuration>
      <person implementation="com.mycompany.mojo.query.SuperPerson">
        <firstName>Jason</firstName>
        <lastName>van Zyl</lastName>
      </person>
    </configuration>
  • Mapping Collections

    • Mapping Lists

      public class MyAnimalMojo extends AbstractMojo {
          @Parameter(property = "animals")
          private List animals;
      
          public void execute() throws MojoExecutionException {
              ...
          }
      }
      <configuration>
        <animals>
          <animal>cat</animal>
          <animal>dog</animal>
          <animal>aardvark</animal>
        </animals>
      </configuration>
    • Mapping Properties

          @Parameter(property = "myProperties")
          private Properties myProperties
      <configuration>
        <myProperties>
          <property>
            <name>propertyName1</name>
            <value>propertyValue1</value>
          </property>
          <property>
            <name>propertyName2</name>
            <value>propertyValue2</value>
          </property>
        </myProperties>
      </configuration>

Configuring Build Plugins

  • Using the <executions> Tag

    <build>
      <plugins>
        <plugin>
          <artifactId>maven-myquery-plugin</artifactId>
          <version>1.0</version>
          <executions>
            <execution>
              <id>execution1</id>
              <phase>test</phase>
              <configuration>
                ...
              </configuration>
              <goals>
                <goal>query</goal>
              </goals>
            </execution>
            <execution>
              <id>execution2</id>
              <configuration>
                ...
              </configuration>
              <goals>
                <goal>query</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </build>
    • The first execution with id "execution1" binds this configuration to the test phase.

    • The second execution does not have a <phase> tag, have a default phase binding.

    • If the goal has a default phase binding then it will execute in that phase.

    • But if the goal is not bound to any lifecycle phase then it simply won’t be executed during the build lifecycle.

    Note that while execution id’s have to be unique among all executions of a single plugin within a POM, they don’t have to be unique across an inheritance hierarchy of POMs.

    Executions of the same id from different POMs are merged.

    The same applies to executions that are defined by profiles.

  • Using the <dependencies> Tag

    You could configure the dependencies of the Build plugins, commonly to use a more recent dependency version.

    For instance, the Maven Antrun Plugin version 1.2 uses Ant version 1.6.5, if you want to use the latest Ant version when running this plugin, you need to add <dependencies> element like the following:

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-antrun-plugin</artifactId>
      <version>1.2</version>
        ...
      <dependencies>
        <dependency>
          <groupId>org.apache.ant</groupId>
          <artifactId>ant</artifactId>
          <version>1.7.1</version>
        </dependency>
        <dependency>
          <groupId>org.apache.ant</groupId>
          <artifactId>ant-launcher</artifactId>
          <version>1.7.1</version>
        </dependency>
      </dependencies>
    </plugin>
  • Using the <inherited> Tag In Build Plugins

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-antrun-plugin</artifactId>
      <version>1.2</version>
      <inherited>false</inherited>
        ...
    </plugin>

3.4. Maven Help Plugin

  • The help:active-profiles Goal

    The active-profiles goal is used to discover which profiles have been applied to the projects currently being built.

    For each project in the build session, it will output a list of profiles which have been applied to that project, along with the source of the profile (POM, settings.xml or profiles.xml).

    You can execute this goal using the following command:

    # mvn help:active-profiles
    you could also use the output parameter to redirect output to a file.
  • The help:all-profiles Goal

    The all-profiles goal is used to discover all available profiles under the current project.

    You can execute this goal using the following command:

    # mvn help:all-profiles
    you could also use the output parameter to redirect output to a file.
  • The help:describe Goal

    The describe goal is used to discover information about Maven plugins.

    Given either a plugin or a groupId, an artifactId and optionally a version, the goal will lookup that plugin and output details about it.

    If the user also specifies which goal to describe, the describe goal will limit output to the details of that goal, including parameters.

    You can execute this goal using the following command:

    # mvn help:describe -DgroupId=org.somewhere -DartifactId=some-plugin -Dversion=0.0.0
    you could also use the output parameter to redirect output to a file.

    Refer to Configuring Describe Goal for more information about its configuration.

  • The help:effective-pom Goal

    The effective-pom goal is used to make visible the POM that results from the application of interpolation, inheritance and active profiles.

    It provides a useful way of removing the guesswork about just what ends up in the POM that Maven uses to build your project.

    It will iterate over all projects in the current build session, printing the effective POM for each.

    You can execute this goal using the following command:

    # mvn help:effective-pom
    you could also use the output parameter to redirect output to a file.
  • The help:effective-settings Goal

    The effective-settings goal is used to view the settings that Maven actually uses to run the build.

    These settings are a result of merging the global file with the user’s file, with the user’s file taking precedence.

    You can execute this goal using the following command:

    # mvn help:effective-settings
    you could also use the output parameter to redirect output to a file.
  • The help:system Goal

    The system goal is used to view the system information like system properties and environment variables.

    You can execute this goal using the following command:

    # mvn help:system
    you could also use the output parameter to redirect output to a file.
  • The help:evaluate Goal

    You could use this interactive goal to evaluate some Maven expressions. To do it, just call the help:evaluate goal:

    # mvn help:evaluate -Dartifact=org.apache.maven.plugins:maven-help-plugin
    ...
    [INFO] [help:evaluate]
    [INFO] Enter the Maven expression i.e. ${project.groupId} or 0 to exit?:
    ${project.artifactId}
    [INFO]
    maven-help-plugin
    [INFO] Enter the Maven expression i.e. ${project.groupId} or 0 to exit?:
    ${project.none}
    [INFO]
    null object or invalid expression
    ...

    The artifact parameter refers to ask expressions on the artifact POM. If omitted, the evaluate goal uses the current pom.

    You could ask for all Maven expressions listed in the Javadoc of the PluginParameterExpressionEvaluator class.

3.5. Spring Boot Maven Plugin

  • Create a Spring MVC project with start.spring.io

    $ curl -sS -o demo.zip "https://start.spring.io/starter.zip?type=maven-project&language=java&bootVersion=2.5.6&baseDir=demo&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo&packaging=jar&javaVersion=11&dependencies=web,devtools,actuator"
    $ unzip demo.zip && cd demo
  • Display help information on spring-boot-maven-plugin.

    $ mvn spring-boot:help
    
    ...
    
    This plugin has 7 goals:
    
    spring-boot:build-image
      Package an application into a OCI image using a buildpack.
    
    spring-boot:build-info
      Generate a build-info.properties file based on the content of the current
      MavenProject.
    
    spring-boot:help
      Display help information on spring-boot-maven-plugin.
      Call mvn spring-boot:help -Ddetail=true -Dgoal=<goal-name> to display
      parameter details.
    
    spring-boot:repackage
      Repackage existing JAR and WAR archives so that they can be executed from the
      command line using java -jar. With layout=NONE can also be used simply to
      package a JAR with nested dependencies (and no main class, so not executable).
    
    spring-boot:run
      Run an application in place.
    
    spring-boot:start
      Start a spring application. Contrary to the run goal, this does not block and
      allows other goals to operate on the application. This goal is typically used
      in integration test scenario where the application is started before a test
      suite and stopped after.
    
    spring-boot:stop
      Stop an application that has been started by the 'start' goal. Typically
      invoked once a test suite has completed.
    
    ...
  • Build and run Spring boot

    $ mvn package
    $ java -Dmanagement.endpoints.web.exposure.include=health -Dserver.port=8088 -jar target/demo-0.0.1-SNAPSHOT.jar

    Open another command shell:

    $ curl -i localhost:8088/actuator/health
    HTTP/1.1 200
    Content-Type: application/vnd.spring-boot.actuator.v3+json
    Transfer-Encoding: chunked
    Date: Mon, 01 Nov 2021 10:52:48 GMT
    
    {"status":"UP"}
  • Show META-INF/MANIFEST.MF of demo-0.0.1-SNAPSHOT.jar

    $ jar -xf target/demo-0.0.1-SNAPSHOT.jar META-INF/MANIFEST.MF
    
    $ cat META-INF/MANIFEST.MF
    Manifest-Version: 1.0
    Created-By: Maven Jar Plugin 3.2.0
    Build-Jdk-Spec: 11
    Implementation-Title: demo
    Implementation-Version: 0.0.1-SNAPSHOT
    Main-Class: org.springframework.boot.loader.JarLauncher
    Start-Class: com.example.demo.DemoApplication
    Spring-Boot-Version: 2.5.6
    Spring-Boot-Classes: BOOT-INF/classes/
    Spring-Boot-Lib: BOOT-INF/lib/
    Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
    Spring-Boot-Layers-Index: BOOT-INF/layers.idx
    

4. Java in Container

The linux free command detects memory info from /proc/meminfo instead of /sys/fs/cgroup/memory/memory.stat, that’s the container total memory is always the same with the virtual machine host total memory.

$ free
              total        used        free      shared  buff/cache   available
Mem:        4017728      726892     1148152        1212     2142684     3026308
Swap:             0           0           0

$ docker run --rm openjdk:8 free
               total        used        free      shared  buff/cache   available
Mem:         4017728      814556     1408108        1284     1795064     2943488
Swap:              0           0           0

$ docker run --rm -m 512m openjdk:11 free
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
               total        used        free      shared  buff/cache   available
Mem:         4017728      816368     1383084        1292     1818276     2941724
Swap:              0           0           0

$ docker run --rm -m 512m openjdk:11 cat /sys/fs/cgroup/memory/memory.limit_in_bytes
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
536870912

$ echo $((536870912 / 1024 / 1024))m
512m

We will use the parallel collector to demostrate the java VM container support. Unless the initial and maximum heap sizes are specified on the command line, they’re calculated based on the amount of memory on the machine. The default maximum heap size is one-fourth of the physical memory while the initial heap size is 1/64th of physical memory. The maximum amount of space allocated to the young generation is one third of the total heap size.

# Run Java in virtual machine host
$ java -XX:+UseParallelGC -XshowSettings:vm -version
VM settings:
    Max. Heap Size (Estimated): 873.00M
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "11.0.12" 2021-07-20
OpenJDK Runtime Environment (build 11.0.12+7-post-Debian-2deb10u1)
OpenJDK 64-Bit Server VM (build 11.0.12+7-post-Debian-2deb10u1, mixed mode, sharing)

4.1. Container detection support

The runtime UseContainerSupport option now provides automatic container detection support, which allows the VM to determine the amount of memory and number of processors that are available to a Java process running in docker containers. It uses this information to allocate system resources. This support is only available on Linux x64 platforms. If supported, the default for this flag is true, and container support is enabled by default. It can be disabled with -XX:-UseContainerSupport.

  • Use -XX:-UseContainerSupport to disable container support

    As we can see, the default java VM maximum heap size is always same with the host, and the maximum heap size 873M is about one fourth of the physical memory 4G.

    • Run Java in container without memory limit

      $ docker run --rm openjdk:11 java -XX:-UseContainerSupport -XX:+UseParallelGC -XshowSettings:vm -version
      VM settings:
          Max. Heap Size (Estimated): 873.00M
          Using VM: OpenJDK 64-Bit Server VM
      
      openjdk version "11.0.13" 2021-10-19
      OpenJDK Runtime Environment 18.9 (build 11.0.13+8)
      OpenJDK 64-Bit Server VM 18.9 (build 11.0.13+8, mixed mode, sharing)
    • Run Java in container with memory limit

      $ docker run --rm -m 512m openjdk:11 java -XX:-UseContainerSupport -XX:+UseParallelGC -XshowSettings:vm -version
      WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
      VM settings:
          Max. Heap Size (Estimated): 873.00M
          Using VM: OpenJDK 64-Bit Server VM
      
      openjdk version "11.0.13" 2021-10-19
      OpenJDK Runtime Environment 18.9 (build 11.0.13+8)
      OpenJDK 64-Bit Server VM 18.9 (build 11.0.13+8, mixed mode, sharing)
  • Use -XX:+UseContainerSupport to enable container support

    The default value for this flag -XX:+UseContainerSupport is true, so we can run java without it. Now, the default maximum heap size is 114M, which is about one fourth of the memory limit 512m.

    • Run Java in container with memory limit

      $ docker run --rm -m 512m openjdk:11 java -XX:+UseParallelGC -XshowSettings:vm -version
      WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
      VM settings:
          Max. Heap Size (Estimated): 114.00M
          Using VM: OpenJDK 64-Bit Server VM
      
      openjdk version "11.0.13" 2021-10-19
      OpenJDK Runtime Environment 18.9 (build 11.0.13+8)
      OpenJDK 64-Bit Server VM 18.9 (build 11.0.13+8, mixed mode, sharing)
    • Use -Xlog:os+container=trace for maximum logging of container information.

      $ docker run --rm -m 512m openjdk:11 java -Xlog:os+container=trace -version
      WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
      [0.000s][trace][os,container] OSContainer::init: Initializing Container Support
      [0.001s][trace][os,container] Path to /memory.use_hierarchy is /sys/fs/cgroup/memory/memory.use_hierarchy
      [0.001s][trace][os,container] Use Hierarchy is: 1
      [0.001s][trace][os,container] Path to /memory.limit_in_bytes is /sys/fs/cgroup/memory/memory.limit_in_bytes
      [0.001s][trace][os,container] Memory Limit is: 536870912
      [0.001s][info ][os,container] Memory Limit is: 536870912
      [0.001s][trace][os,container] Path to /cpu.cfs_quota_us is /sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us
      [0.001s][trace][os,container] CPU Quota is: -1
      [0.001s][trace][os,container] Path to /cpu.cfs_period_us is /sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us
      [0.001s][trace][os,container] CPU Period is: 100000
      [0.001s][trace][os,container] Path to /cpu.shares is /sys/fs/cgroup/cpu,cpuacct/cpu.shares
      [0.001s][trace][os,container] CPU Shares is: 1024
      [0.001s][trace][os,container] OSContainer::active_processor_count: 2
      [0.001s][trace][os,container] OSContainer::active_processor_count (cached): 2
      [0.003s][trace][os,container] OSContainer::active_processor_count (cached): 2
      [0.039s][trace][os,container] Path to /cpu.cfs_quota_us is /sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us
      [0.040s][trace][os,container] CPU Quota is: -1
      [0.040s][trace][os,container] Path to /cpu.cfs_period_us is /sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us
      [0.040s][trace][os,container] CPU Period is: 100000
      [0.040s][trace][os,container] Path to /cpu.shares is /sys/fs/cgroup/cpu,cpuacct/cpu.shares
      [0.041s][trace][os,container] CPU Shares is: 1024
      [0.041s][trace][os,container] OSContainer::active_processor_count: 2
      [0.063s][trace][os,container] Path to /memory.limit_in_bytes is /sys/fs/cgroup/memory/memory.limit_in_bytes
      [0.064s][trace][os,container] Memory Limit is: 536870912
      [0.064s][trace][os,container] Path to /memory.usage_in_bytes is /sys/fs/cgroup/memory/memory.usage_in_bytes
      [0.065s][trace][os,container] Memory Usage is: 10055680
      
      ...
      
      openjdk version "11.0.13" 2021-10-19
      OpenJDK Runtime Environment 18.9 (build 11.0.13+8)
      OpenJDK 64-Bit Server VM 18.9 (build 11.0.13+8, mixed mode, sharing)

4.2. Spring Boot in Kubernetes

  1. Create Spring MVC project with start.spring.io

    $ curl -sS -o demo.zip "https://start.spring.io/starter.zip?type=maven-project&language=java&bootVersion=2.5.6&baseDir=demo&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo&packaging=jar&javaVersion=11&dependencies=web,devtools,actuator"
    $ unzip demo.zip && cd demo
  2. Build OCI image with Dockerfile

    # Dockerfile
    FROM openjdk:11
    WORKDIR /app
    COPY ./target/*.jar /app/app.jar
    CMD ["java", "-jar", "/app/app.jar"]
    $ mvn package && docker build . -t demo:0.0.1-SNAPSHOT
    [INFO] Scanning for projects...
    [INFO]
    [INFO] --------------------------< com.example:demo >--------------------------
    [INFO] Building demo 0.0.1-SNAPSHOT
    [INFO] --------------------------------[ jar ]---------------------------------
    
    . . .
    
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  14.242 s
    [INFO] Finished at: 2021-11-02T17:49:38+08:00
    [INFO] ------------------------------------------------------------------------
    Sending build context to Docker daemon  19.66MB
    Step 1/4 : FROM openjdk:11
     ---> 40eccaa4f420
    Step 2/4 : WORKDIR /app
     ---> Running in 69e8c68d3924
    Removing intermediate container 69e8c68d3924
     ---> c2ce7e058438
    Step 3/4 : COPY ./target/*.jar /app/app.jar
     ---> 8b4c2e01ea26
    Step 4/4 : CMD ["java", "-jar", "/app/app.jar"]
     ---> Running in e4cf2f54f7ba
    Removing intermediate container e4cf2f54f7ba
     ---> d244def82917
    Successfully built d244def82917
    Successfully tagged demo:0.0.1-SNAPSHOT
    $ docker run --rm --name demo -d -p 8088:8080 demo:0.0.1-SNAPSHOT
    d16f5962bbf5ce64eee87ca37b0e94613dcc22a93069ef95dc678dc82fb4962e
    
    $ curl -i localhost:8088/actuator/health
    HTTP/1.1 200
    Content-Type: application/vnd.spring-boot.actuator.v3+json
    Transfer-Encoding: chunked
    Date: Tue, 02 Nov 2021 09:51:11 GMT
    
    {"status":"UP"}
    
    $ docker stop  demo0
    demo
  3. Build OCI image with multiple layers with layertools

    To make it easier to create optimized Docker images, Spring Boot supports adding a layer index file to the jar. It provides a list of layers and the parts of the jar that should be contained within them. The list of layers in the index is ordered based on the order in which the layers should be added to the Docker/OCI image. Out-of-the-box, the following layers are supported:

    • dependencies (for regular released dependencies)

    • spring-boot-loader (for everything under org/springframework/boot/loader)

    • snapshot-dependencies (for snapshot dependencies)

    • application (for application classes and resources)

    $ java -Djarmode=layertools \
    > -jar target/demo-0.0.1-SNAPSHOT.jar extract --destination layers
    
    $ tree -L 3 layers/
    layers/
    ├── application
    │   ├── BOOT-INF
    │   │   ├── classes
    │   │   ├── classpath.idx
    │   │   └── layers.idx
    │   └── META-INF
    │       ├── MANIFEST.MF
    │       └── maven
    ├── dependencies
    │   └── BOOT-INF
    │       └── lib
    ├── snapshot-dependencies
    └── spring-boot-loader
        └── org
            └── springframework

    This layering is designed to separate code based on how likely it is to change between application builds. Library code is less likely to change between builds, so it is placed in its own layers to allow tooling to re-use the layers from cache. Application code is more likely to change between builds so it is isolated in a separate layer.

    # Dockerfile.layers
    FROM openjdk:11 as builder
    WORKDIR /app
    COPY target/*.jar app.jar
    RUN java -Djarmode=layertools -jar app.jar extract
    
    FROM openjdk:11
    WORKDIR /app
    COPY --from=builder /app/dependencies/ ./
    COPY --from=builder /app/spring-boot-loader/ ./
    COPY --from=builder /app/snapshot-dependencies/ ./
    COPY --from=builder /app/application/ ./
    
    CMD ["java", "org.springframework.boot.loader.JarLauncher"]
    $ docker build . -t demo:0.0.1-SNAPSHOT -f Dockerfile.layers
    Sending build context to Docker daemon  19.66MB
    Step 1/11 : FROM openjdk:11 as builder
     ---> 40eccaa4f420
    Step 2/11 : WORKDIR /app
     ---> Running in e25debcc04d8
    Removing intermediate container e25debcc04d8
     ---> 7fbb05a599db
    Step 3/11 : COPY target/*.jar app.jar
     ---> 2a9ae411237d
    Step 4/11 : RUN java -Djarmode=layertools -jar app.jar extract
     ---> Running in a47dcec36428
    Removing intermediate container a47dcec36428
     ---> 5eb010818830
    Step 5/11 : FROM openjdk:11
     ---> 40eccaa4f420
    Step 6/11 : WORKDIR /app
     ---> Using cache
     ---> 7fbb05a599db
    Step 7/11 : COPY --from=builder /app/dependencies/ ./
     ---> 829945939519
    Step 8/11 : COPY --from=builder /app/spring-boot-loader/ ./
     ---> cc00e84c914e
    Step 9/11 : COPY --from=builder /app/snapshot-dependencies/ ./
     ---> d2171d6819c6
    Step 10/11 : COPY --from=builder /app/application/ ./
     ---> 31ff9f69f026
    Step 11/11 : CMD ["java", "org.springframework.boot.loader.JarLauncher"]
     ---> Running in 897e8650809c
    Removing intermediate container 897e8650809c
     ---> 49fa23a24b42
    Successfully built 49fa23a24b42
    Successfully tagged demo:0.0.1-SNAPSHOT
    $ docker run --rm --name demo -d -p 8088:8080 demo:0.0.1-SNAPSHOT
    4c6a0c6e90d0d74632a7a76e028ae1b85d0ab693903b046dfe535615f0908b43
    
    $ curl -i localhost:8088/actuator/health
    HTTP/1.1 200
    Content-Type: application/vnd.spring-boot.actuator.v3+json
    Transfer-Encoding: chunked
    Date: Tue, 02 Nov 2021 10:03:57 GMT
    
    {"status":"UP"}
    
    $ docker stop demo
    demo
  4. Build OCI image with mvn spring-boot:build-image

    $ mvn spring-boot:build-image
    [INFO] Scanning for projects...
    [INFO]
    [INFO] --------------------------< com.example:demo >--------------------------
    [INFO] Building demo 0.0.1-SNAPSHOT
    [INFO] --------------------------------[ jar ]---------------------------------
    
    ...
    
    [INFO]     [creator]     Paketo BellSoft Liberica Buildpack 8.9.0
    [INFO]     [creator]       https://github.com/paketo-buildpacks/bellsoft-liberica
    [INFO]     [creator]       Build Configuration:
    [INFO]     [creator]         $BP_JVM_TYPE                 JRE             the JVM type - JDK or JRE
    [INFO]     [creator]         $BP_JVM_VERSION              11.*            the Java version
    [INFO]     [creator]       Launch Configuration:
    [INFO]     [creator]         $BPL_DEBUG_ENABLED           false           enables Java remote debugging support
    [INFO]     [creator]         $BPL_DEBUG_PORT              8000            configure the remote debugging port
    [INFO]     [creator]         $BPL_DEBUG_SUSPEND           false           configure whether to suspend execution until a debugger has attached
    [INFO]     [creator]         $BPL_HEAP_DUMP_PATH                          write heap dumps on error to this path
    [INFO]     [creator]         $BPL_JAVA_NMT_ENABLED        true            enables Java Native Memory Tracking (NMT)
    [INFO]     [creator]         $BPL_JAVA_NMT_LEVEL          summary         configure level of NMT, summary or detail
    [INFO]     [creator]         $BPL_JFR_ARGS                                configure custom Java Flight Recording (JFR) arguments
    [INFO]     [creator]         $BPL_JFR_ENABLED             false           enables Java Flight Recording (JFR)
    [INFO]     [creator]         $BPL_JMX_ENABLED             false           enables Java Management Extensions (JMX)
    [INFO]     [creator]         $BPL_JMX_PORT                5000            configure the JMX port
    [INFO]     [creator]         $BPL_JVM_HEAD_ROOM           0               the headroom in memory calculation
    [INFO]     [creator]         $BPL_JVM_LOADED_CLASS_COUNT  35% of classes  the number of loaded classes in memory calculation
    [INFO]     [creator]         $BPL_JVM_THREAD_COUNT        250             the number of threads in memory calculation
    [INFO]     [creator]         $JAVA_TOOL_OPTIONS                           the JVM launch flags
    
    ...
    
    [INFO]     [creator]     Saving docker.io/library/demo:0.0.1-SNAPSHOT...
    [INFO]     [creator]     *** Images (702b824ba18f):
    [INFO]     [creator]           docker.io/library/demo:0.0.1-SNAPSHOT
    [INFO]
    [INFO] Successfully built image 'docker.io/library/demo:0.0.1-SNAPSHOT'
    [INFO]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  15.435 s
    [INFO] Finished at: 2021-11-02T14:35:32+08:00
    [INFO] ------------------------------------------------------------------------
  5. Deploy demo.app into Kubernetes

    • unable to calculate memory configuration

      # demo.yaml
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        labels:
          app: demo
        name: demo
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: demo
        template:
          metadata:
            labels:
              app: demo
          spec:
            containers:
              - name: demo
                image: demo:0.0.1-SNAPSHOT
                resources:
                  requests:
                    cpu: 100m
                    memory: 128Mi
                  limits:
                    cpu: 250m
                    memory: 256Mi
      $ kubectl apply -f demo.yaml
      deployment.apps/demo created
      
      $ kubectl get po demo-f74fb85d9-gh28w
      NAME                   READY   STATUS   RESTARTS      AGE
      demo-f74fb85d9-gh28w   0/1     Error    3 (34s ago)   52s
      
      $ kubectl logs demo-f74fb85d9-gh28w
      Setting Active Processor Count to 2
      unable to calculate memory configuration
      fixed memory regions require 597169K which is greater than 256M available for allocation: -XX:MaxDirectMemorySize=10M, -XX:MaxMetaspaceSize=85169K, -XX:ReservedCodeCacheSize=240M, -Xss1M * 250 threads
      ERROR: failed to launch: exec.d: failed to execute exec.d file at path '/layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/memory-calculator': exit status 1
    • Java VM Garbage Collection Tuning

      # demo.yaml
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        labels:
          app: demo
        name: demo
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: demo
        template:
          metadata:
            labels:
              app: demo
          spec:
            containers:
              - name: demo
                image: demo:0.0.1-SNAPSHOT
                env:
                  - name: JAVA_TOOL_OPTIONS
                    value: "-XX:MaxDirectMemorySize=8M -XX:MaxMetaspaceSize=64M -XX:ReservedCodeCacheSize=16M -Xss512K"
                resources:
                  requests:
                    cpu: 100m
                    memory: 128Mi
                  limits:
                    cpu: 250m
                    memory: 256Mi
      $ kubectl get po -l app=demo
      NAME                    READY   STATUS    RESTARTS   AGE
      demo-7b848bcfd6-82lms   1/1     Running   0          25s
      
      $ kubectl logs -f demo-7b848bcfd6-82lms
      Setting Active Processor Count to 2
      Calculated JVM Memory Configuration: -Xmx43M (Total Memory: 256M, Thread Count: 250, Loaded Class Count: 12623, Headroom: 0%)
      Enabling Java Native Memory Tracking
      Adding 128 container CA certificates to JVM truststore
      Spring Cloud Bindings Enabled
      Picked up JAVA_TOOL_OPTIONS: -XX:MaxDirectMemorySize=8M -XX:MaxMetaspaceSize=64M -XX:ReservedCodeCacheSize=16M -Xss512K -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -XX:+ExitOnOutOfMemoryError -XX:ActiveProcessorCount=2 -Xmx43M -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics -Dorg.springframework.cloud.bindings.boot.enable=true
      
      ...
      
      2021-11-02 07:34:06.238  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
      2021-11-02 07:34:06.432  INFO 1 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 28.591 seconds (JVM running for 32.397)
      $ kubectl expose deployment demo --port 8080 --type NodePort
      service/demo exposed
      
      $ kubectl get svc -l app=demo
      NAME   TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
      demo   NodePort   10.99.172.195   <none>        8080:30227/TCP   6s
      
      $ curl -i localhost:30227
      HTTP/1.1 404
      Vary: Origin
      Vary: Access-Control-Request-Method
      Vary: Access-Control-Request-Headers
      Content-Type: application/json
      Transfer-Encoding: chunked
      Date: Tue, 02 Nov 2021 07:42:29 GMT
      
      {"timestamp":"2021-11-02T07:42:29.133+00:00","status":404,"error":"Not Found","path":"/"}
    • Liveness and Readiness Probes with Spring Boot

      # demo.yaml
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        labels:
          app: demo
        name: demo
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: demo
        template:
          metadata:
            labels:
              app: demo
          spec:
            containers:
              - name: demo
                image: demo:0.0.1-SNAPSHOT
                env:
                  - name: JAVA_TOOL_OPTIONS
                    value: "-XX:MaxDirectMemorySize=8M -XX:MaxMetaspaceSize=64M -XX:ReservedCodeCacheSize=16M -Xss512K"
                args:
                  - Dmanagement.endpoint.health.group.health.include=readiness,liveness
                  - Dmanagement.endpoints.web.exposure.include=health
                livenessProbe:
                  initialDelaySeconds: 60
                  httpGet:
                    path: /actuator/health/liveness
                    port: 8080
                readinessProbe:
                  initialDelaySeconds: 60
                  httpGet:
                    path: /actuator/health/readiness
                    port: 8080
                resources:
                  requests:
                    cpu: 100m
                    memory: 128Mi
                  limits:
                    cpu: 250m
                    memory: 256Mi
      $ kubectl get po -l app=demo
      NAME                    READY   STATUS    RESTARTS   AGE
      demo-5f9cd9c556-mwkrx   1/1     Running   0          110s
      
      $ curl -i localhost:30227/actuator/health
      HTTP/1.1 200
      Content-Type: application/vnd.spring-boot.actuator.v3+json
      Transfer-Encoding: chunked
      Date: Tue, 02 Nov 2021 07:48:07 GMT
      
      {"status":"UP","groups":["liveness","readiness"]}