Skip to main content

Java Programming Language: Overview, Java and Maven Installation, Java Example Application, Java Maven Example Application, Package Application as Thin and Fat Jar, Containerize the Package, Multistage Dockerfile

2708 words·
Java Maven Thin Jar Fat Jar OpenJDK Linux Multistage Dockerfile
Table of Contents

This blog post is about the fundamentals of Java Maven. The focus is on the manually steps, from the creation of a project, to the package of the application. I also provide a multistage Dockerfile, that combines the these steps.


Java Overview
#

Java Language
#

  • Object-oriented programming language that can run on many operating systems without modification.

Core Components:

  • Java Virtual Machine (JVM): Executes Java programs, providing a platform-independent runtime.

  • Java Development Kit (JDK): A toolkit for developing Java applications (includes the compiler, libraries, and JVM).

  • Java Runtime Environment (JRE): A subset of the JDK that includes only what’s needed to run Java applications.


OpenJDK
#

  • Open-source version of Java SE (Java Platform Standard Edition)

  • Alternative to the official Java implementation by Oracle, which has a more restrictive license.

OpenJDK Components:

  • Java Developer Kit (JDK): Includes the Javac compiler, used for compiling Java into bytecode. Necessary to create stand-alone Java executables.

  • Java Runtime Environment (JRE): Executes Java code and compiled Java bytecode.

  • To only run Java programs, JRE can be used without the JDK.


Maven
#

  • Build automation tool and dependency management tool for Java projects. Simplifies and standardizes the build process of Java applications.

  • Compiles source code into executable code like .class or .jar files

  • Maven automatically downloads and manages external libraries / dependencies from a pom.xml configuration file, that are needed for the project.

  • Provides a standardized project structure (e.g., where to place source code, resources and tests).



Java Setup (Linux Server)
#

Java Development Kit (JDK) Installation
#

# Find the latest OpenJDK version
https://jdk.java.net/
# Install OpenJDK version 17
sudo apt install openjdk-17-jdk -y

# Optional, install only the JRE component of OpenJDK
sudo apt install openjdk-17-jre -y

Verify Java installation
#

# Verify installation / check version
java -version

# Shell output:
openjdk version "17.0.13" 2024-10-15
OpenJDK Runtime Environment (build 17.0.13+11-Ubuntu-2ubuntu124.04)
OpenJDK 64-Bit Server VM (build 17.0.13+11-Ubuntu-2ubuntu124.04, mixed mode, sharing)
# Verify Java compiler / check version (Only with JDK installation, not with JRE only)
javac -version

# Shell output:
javac 17.0.13

Set Java Environment Variables
#

Current user:

# Append .bashrc of the current user
echo -e 'export JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))' >> ~/.bashrc
echo -e 'export PATH=$PATH:$JAVA_HOME/bin' >> ~/.bashrc

# Apply changes
source ~/.bashrc
# Verify environemtn variables
echo $JAVA_HOME
echo $PATH

# Shell output:
/usr/lib/jvm/java-17-openjdk-amd64
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/lib/jvm/java-17-openjdk-amd64/bin



Java Maven Setup (Linux Server)
#

  • Java Development Kit (JDK) must be installed

Maven Installation
#

# Install Maven
sudo apt install maven -y

Verify Installation
#

# Verify the Installation
mvn -version

# Shell output:
Apache Maven 3.8.7
Maven home: /usr/share/maven
Java version: 17.0.13, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "6.8.0-49-generic", arch: "amd64", family: "unix"

Maven Commands Overview
#

# Clean up the "target/" directory
mvn clean

# Verify the "pom.xml" is correct
mvn validate

# Compile the source code
mvn compile

# Run unit tests
mvn test

# Package the compiled code into a JAR or WAR
mvn package



Java Example Application
#

Create Application
#

# Create a simple hello world application
vi HelloWorld.java
// Define public class named 'HelloWorld'. The class name must match the file name (HelloWorld.java).
public class HelloWorld {

    // Entry point of the Java application
    public static void main(String[] args) {
        // Standard output stream, print sting to the console
        System.out.println("Hi there, Java test");
    }
}

Run Application Using the (OpenJDK) JRE
#

Use the previously installed(OpenJDK) JRE to run the Java code:

# Run a Java Application
java HelloWorld.java

# Shell output:
Hi there, Java test

Compile & Run Application Using the JDK / JRE
#

Compile Java code into Java bytecode:

# Compile with Javac compiler
javac HelloWorld.java
# Run the compiled class with JRE
java HelloWorld

# Shell output:
Hi there, Java test



Java Maven Example Application
#

Create Project
#

# Create a new Maven project: Standard Maven project structure
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=HelloMaven-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.5 -DinteractiveMode=false
  • DgroupId Unique identifier for the project, typically your domain name in reverse

  • -DartifactId The name of your project, for example HelloMaven-app


Verify Project Structure
#

# Navigate into the project
cd HelloMaven-app
HelloMaven-app
├── pom.xml  # Maven configuration file
└── src
    ├── main
    │   └── java
    │       └── com
    │           └── mycompany
    │               └── app
    │                   └── App.java  # Main Java file
    └── test
        └── java
            └── com
                └── mycompany
                    └── app
                        └── AppTest.java  # Test file
  • pom.xml Core of a project configuration in Maven.

Project Files
#

Main Java File
#

# Open the main Java file "App.java"
vi src/main/java/com/mycompany/app/App.java
// Define the package name for this class: A package is a namespace that organizes related classes and interfaces
package com.mycompany.app;

// Import the DateTime class from the Joda-Time library
import org.joda.time.DateTime;

// Define a public class named "App"
public class App {
    // Print some text
    public static void main(String[] args) {
        System.out.println("Hello, from Maven. Time:");

        // Use Joda-Time to get and print the current date and time
        DateTime currentTime = new DateTime();
        System.out.println(currentTime.toString("yyyy-MM-dd HH:mm:ss"));
    }
}

Add Dependencies to pom.xml
#

Add the Joda and Junit test dependencies to pom.xml:

# Open "pom.xml"
vi pom.xml

The original “dependency” configuration looks like this:

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.junit</groupId>
        <artifactId>junit-bom</artifactId>
        <version>5.11.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <scope>test</scope>
    </dependency>
    <!-- Optionally: parameterized tests support -->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-params</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

Add the Joda and Junit test dependencies:

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.junit</groupId>
        <artifactId>junit-bom</artifactId>
        <version>5.11.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <scope>test</scope>
    </dependency>
    <!-- Optionally: parameterized tests support -->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-params</artifactId>
      <scope>test</scope>
    </dependency>
  # Add Joda & Junit test dependency 
    <!-- Joda-Time dependency -->
    <dependency>
      <groupId>joda-time</groupId>
      <artifactId>joda-time</artifactId>
      <version>2.10.14</version>
    </dependency>
    <!-- Junit dependency -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

Add Plugins to pom.xml
#

  • The default behavior of Maven does not include dependencies in the packaged JAR file (thin JAR).

  • Add the maven-assembly-plugin to pom.xml bundle the dependencies into the final JAR, creating a fat JAR that can run without needing external dependencies.

  • This plugin simplifies deployment by producing a standalone, self-contained JAR file

# Open "pom.xml"
vi pom.xml

The original “plugins” sections looks like this:

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.4.0</version>
        </plugin>
        <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.3.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.13.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>3.3.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.4.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>3.1.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>3.1.2</version>
        </plugin>
        <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
        <plugin>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.12.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
          <version>3.6.1</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>

Add the maven-assembly-plugin:

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.4.0</version>
        </plugin>
        <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.3.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.13.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>3.3.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.4.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>3.1.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>3.1.2</version>
        </plugin>
        <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
        <plugin>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.12.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
          <version>3.6.1</version>
        </plugin>

        <!-- Add maven-assembly-plugin -->
        <plugin>
          <artifactId>maven-assembly-plugin</artifactId>
          <configuration>
            <archive>
              <manifest>
                <mainClass>com.mycompany.app.App</mainClass>
              </manifest>
            </archive>
            <descriptorRefs>
              <descriptorRef>jar-with-dependencies</descriptorRef>
            </descriptorRefs>
          </configuration>
        </plugin>

      </plugins>
    </pluginManagement>
  </build>

Adapt Unit Test
#

# Adopt the Test
vi src/test/java/com/mycompany/app/AppTest.java
package com.mycompany.app;

import static org.junit.Assert.assertTrue;

import org.joda.time.DateTime;
import org.junit.Test;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

public class AppTest {

    @Test
    public void testMainOutput() {
        // Capture the console output
        ByteArrayOutputStream outContent = new ByteArrayOutputStream();
        System.setOut(new PrintStream(outContent));

        // Run the main method
        App.main(new String[]{});

        // Verify the output contains the greeting
        String output = outContent.toString();
        assertTrue("Output should contain the greeting", output.contains("Hello, from Maven. Time:"));

        // Verify the output contains a correctly formatted timestamp
        DateTime now = new DateTime();
        String expectedDate = now.toString("yyyy-MM-dd");
        assertTrue("Output should contain the current date", output.contains(expectedDate));

        // Reset the console output
        System.setOut(System.out);
    }
}

Build the Example project
#

Run the Unit Test
#

# Run the test
mvn test

# Shell output:
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< com.mycompany.app:HelloMaven-app >------------------
[INFO] Building HelloMaven-app 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:3.3.1:resources (default-resources) @ HelloMaven-app ---
[INFO] skip non existing resourceDirectory /home/ubuntu/HelloMaven-app/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.13.0:compile (default-compile) @ HelloMaven-app ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 1 source file with javac [debug release 17] to target/classes
[INFO]
[INFO] --- maven-resources-plugin:3.3.1:testResources (default-testResources) @ HelloMaven-app ---
[INFO] skip non existing resourceDirectory /home/ubuntu/HelloMaven-app/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.13.0:testCompile (default-testCompile) @ HelloMaven-app ---
[INFO] Recompiling the module because of changed dependency.
[INFO] Compiling 1 source file with javac [debug release 17] to target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:3.3.0:test (default-test) @ HelloMaven-app ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.mycompany.app.AppTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.022 s -- in com.mycompany.app.AppTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.815 s
[INFO] Finished at: 2024-11-26T21:10:24Z
[INFO] ------------------------------------------------------------------------

Run the Application Without Compiling
#

Run the application without without manually compiling it (Direct Execution). Maven handles the build and execution in one step:

# Run the application
mvn exec:java -Dexec.mainClass="com.mycompany.app.App"

# Shell output:
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< com.mycompany.app:HelloMaven-app >------------------
[INFO] Building HelloMaven-app 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- exec-maven-plugin:3.5.0:java (default-cli) @ HelloMaven-app ---
Hello, from Maven. Time:
2024-11-26 21:20:03
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.214 s
[INFO] Finished at: 2024-11-26T21:20:03Z
[INFO] ------------------------------------------------------------------------
  • mvn exec:java Runs the exec plugin to execute the application.

  • -Dexec.mainClass="com.mycompany.app.App" Specifies the fully qualified name of the main class.


Build the Application: Without Dependencies
#

Compile the Application
#

Compile the Java source code of the project into Java bytecode (.class files) that can be executed by the Java Virtual Machine (JVM):

  • Maven checks the dependencies section in the pom.xml file and downloads any missing dependencies.

  • Maven looks for .java files in the src/main/java directory and ompiles these files into .class files using the JDK’s Javac compiler.

  • Compiled .class files are placed in the target/classes directory

# Compile the application
mvn compile

# Shell output:
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< com.mycompany.app:HelloMaven-app >------------------
[INFO] Building HelloMaven-app 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:3.3.1:resources (default-resources) @ HelloMaven-app ---
[INFO] skip non existing resourceDirectory /home/ubuntu/HelloMaven-app/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.13.0:compile (default-compile) @ HelloMaven-app ---
[INFO] Nothing to compile - all classes are up to date.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.188 s
[INFO] Finished at: 2024-11-26T21:29:48Z
[INFO] ------------------------------------------------------------------------

Verify the file and folder structure:

HelloMaven-app
├── pom.xml  # Maven configuration file
├── src
│   ├── main
│   │   └── java
│   │       └── com
│   │           └── mycompany
│   │               └── app
│   │                   └── App.java  # Main Java file
│   └── test
│       └── java
│           └── com
│               └── mycompany
│                   └── app
│                       └── AppTest.java  # Test file
└── target
    ├── classes
    │   └── com
    │       └── mycompany
    │           └── app
    │               └── App.class  # Verify .class file
    ├── generated-sources
    │   └── annotations
    ├── generated-test-sources
    │   └── test-annotations
    ├── maven-status
    │   └── maven-compiler-plugin
    │       ├── compile
    │       │   └── default-compile
    │       │       ├── createdFiles.lst
    │       │       └── inputFiles.lst
    │       └── testCompile
    │           └── default-testCompile
    │               ├── createdFiles.lst
    │               └── inputFiles.lst
    ├── surefire-reports
    │   ├── com.mycompany.app.AppTest.txt
    │   └── TEST-com.mycompany.app.AppTest.xml
    └── test-classes
        └── com
            └── mycompany
                └── app
                    └── AppTest.class

Package the Application (Thin JAR)
#

Create a distributable version of the application, such as a JAR (Java Archive) or WAR (Web Application Archive) file, which can be deployed or run:

  • The format is defined by the <packaging> tag in pom.xml. By default, it’s JAR.

  • Ensures the pom.xml file is valid and that all dependencies are resolved.

  • If not already done, Maven compiles the source files into bytecode and places them in the target/classes directory.

  • If tests are defined in the src/test/java directory, Maven runs them.

  • The packaged file, for example HelloMaven-app-1.0-SNAPSHOT.jar is placed in the target/ directory.

#  Package the application
mvn package

# Shell output:
...
[INFO] Building jar: /home/ubuntu/HelloMaven-app/target/HelloMaven-app-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.019 s
[INFO] Finished at: 2024-11-26T21:56:06Z
[INFO] ------------------------------------------------------------------------

Build the Application: With Dependencies
#

Compile / Build the Application (Fat JAR)
#

# Build a standalone JAR file
mvn clean compile assembly:single
  • clean Deletes any existing compiled files and build artifacts from the target directory to ensure a fresh build.

  • compile Compiles the Java source code in the src/main/java directory into .class files and places them in the target/classes directory.

  • assembly:single Invokes the maven-assembly-plugin to create a fat JAR that includes the compiled application code and all required dependencies bundled together.

  • The resulting JAR is placed in the target directory: HelloMaven-app-1.0-SNAPSHOT-jar-with-dependencies.jar


Run the Packaged Application
#

# Run the resulting JAR file
java -jar target/HelloMaven-app-1.0-SNAPSHOT-jar-with-dependencies.jar

# Shell output:
Hello, from Maven. Time:
2024-11-27 09:56:15

Containerize the Packaged Application
#

Thin Jar
#

  • By default, Maven packages only the application’s compiled code in the JAR (thin JAR).

  • Required dependencies like joda-time are not bundled inside the JAR and must be provided separately.

  • The following example copies the dependencies into the container


Generate the Dependencies
#

# Use Maven to copy all runtime dependencies to a directory
mvn dependency:copy-dependencies -DoutputDirectory=target/dependency

Dockerfile
#

# Create a Dockerfile
vi Dockerfile
# Use an official OpenJDK runtime as a base image
FROM openjdk:17-jdk-slim

# Set the working directory
WORKDIR /app

# Copy the application's thin JAR to the image
COPY target/HelloMaven-app-1.0-SNAPSHOT.jar app.jar

# Copy all dependencies to the image
COPY target/dependency /app/dependency

# Command to run the application
CMD ["java", "-cp", "app.jar:/app/dependency/*", "com.mycompany.app.App"]

Build Container Image
#

# Build the container image
docker build -t hello-maven-app .

Verify the Container Image
#

# List images
docker images

# Shell output:
REPOSITORY        TAG       IMAGE ID       CREATED         SIZE
hello-maven-app   latest    ec8a509bb53d   4 seconds ago   410MB

Run the Container
#

# Run the container: 
docker run --rm hello-maven-app

# Shell output:
Hello, from Maven. Time:
2024-11-27 10:05:35
  • --rm Automatically removes the container after it stops.



Fat Jar
#

  • The Fat Jar includes all it’s necessary dependencies

Dockerfile
#

# Create a Dockerfile
vi Dockerfile
# Use a lightweight JRE image as the base image
FROM eclipse-temurin:17-jre-alpine

# Set the working directory
WORKDIR /app

# Copy the application's thin JAR to the image
COPY target/HelloMaven-app-1.0-SNAPSHOT-jar-with-dependencies.jar app.jar

# Command to run the application
CMD ["java", "-jar", "app.jar"]

Build Container Image
#

# Build the container image
docker build -t hello-maven-app-thin-jar .

Verify the Container Image
#

# List images
docker images

# Shell output:
REPOSITORY                 TAG       IMAGE ID       CREATED         SIZE
hello-maven-app-thin-jar   latest    acbaa3dc6923   4 seconds ago   186MB
hello-maven-app            latest    ec8a509bb53d   5 minutes ago   410MB

Run the Container
#

# Run the container: 
docker run --rm hello-maven-app-thin-jar

# Shell output:
Hello, from Maven. Time:
2024-11-27 10:10:41
  • --rm Automatically removes the container after it stops.

Multistage Dockerfile
#

Dockerfile
#

  • Build Stage: The application is built in a dedicated environment with all necessary tools and dependencies.

  • Runtime Stage: A lightweight base image eclipse-temurin:17-jre-alpine is used to copy only the compiled Fat Jar from the build stage.

# Create a Dockerfile
vi Dockerfile
### Stage 1: Build Stage

# Use a lightweight JRE image as the base image / define as build stage
FROM maven:3.9.4-eclipse-temurin-17 AS build

# Set the working directory inside the build stage
WORKDIR /app

# Copy the Maven project files into the container
COPY pom.xml ./
COPY src ./src

# Build the application with dependencies included
RUN mvn clean compile assembly:single



### Stage 2: Runtime Stage

# Use a lightweight JRE image as the base image
FROM eclipse-temurin:17-jre-alpine

# Set the working directory inside the runtime stage
WORKDIR /app

# Copy the built JAR file from the build stage to the runtime stage
COPY --from=build /app/target/HelloMaven-app-1.0-SNAPSHOT-jar-with-dependencies.jar app.jar

# Expose port 80 for the web server
EXPOSE 80

# Command to run the application
ENTRYPOINT ["java", "-jar", "app.jar"]

Build Container Image
#

# Build the container image
docker build -t maven-webserver-multistage .

Verify the Container Image
#

# List images
docker images

# Shell output:
REPOSITORY                   TAG       IMAGE ID       CREATED          SIZE
maven-webserver-multistage   latest    f9e9fe1d8aa2   12 seconds ago   189MB

Run the Container
#

# Run the container: 
docker run --rm maven-webserver-multistage

# Shell output:
Hello, from Maven. Time:
2024-11-27 13:47:55