How to compile and run Java in Container
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 thepublic 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.Thejar
commandjar [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
, acolon
(:), or anequal 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
-
validate the project,
-
then will try to compile the sources,
-
run those against the tests,
-
package the binaries (e.g. jar),
-
run integration tests against that package,
-
verify the integration tests,
-
install the verified package to the local repository,
-
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>
TagYou 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 supportAs 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 supportThe 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
-
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
-
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
-
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
-
-
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] ------------------------------------------------------------------------
-
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"]}
-
5. References
-
https://docs.oracle.com/en/java/javase/17/docs/specs/man/index.html
-
https://docs.oracle.com/en/java/javase/17/gctuning/parallel-collector1.html
-
https://maven.apache.org/plugins/maven-help-plugin/usage.html
-
https://maven.apache.org/plugins/maven-dependency-plugin/usage.html
-
https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html