I have managed to setup and run a Docker Image for a simple Hello World Java console application, but I am struggling with it when I add packages. I have this working:
Folder contents:
Dockerfile HelloWorld.class HelloWorld.java
Dockerfile:
FROM openjdk:11-jdk-slim
WORKDIR /app
COPY HelloWorld.java .
RUN javac HelloWorld.java
CMD ["java", "HelloWorld"]
Java class:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, Docker World!");
}
}
This works fine with
sudo docker build -t hello-world-java .
sudo docker run hello-world-java
But if I add a package for the class to the folder contents and the class:
Java class:
package hello;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, Docker World!");
}
}
Contents
~/workspace/helloWorldPackage$ tree
.
├── Dockerfile
└── hello
├── HelloWorld.class
└── HelloWorld.java
2 directories, 3 files
And modify Dockerfile like:
FROM openjdk:11-jdk-slim
WORKDIR /app
COPY hello/ .
RUN javac HelloWorld.java
CMD ["java", "hello.HelloWorld"]
I can docker build it, but when running I get:
Error: Could not find or load main class hello.HelloWorld
Caused by: java.lang.ClassNotFoundException: hello.HelloWorld
exit status 1
I have tried several modifications in the Dockerfile to no avail. Mostly modifying the classpath on the CMD, or what it is copied in COPY/WORKDIR, but I think i do not understand exactly what such commands do. Is CMD run from inside the folder at WORKDIR? How does it interact with the COPY folder? And with the java command? I think there is something I lack there to make it work.
I can only find tutorials or answers for Java web apps or Java local apps without packages. I am expecting a similar behavior to what I get for a packageless Java application. I.e. the correct Dockerfile description for java to catch the correct classpath.
When you try to launch hello.HelloWorld
as the main class, the JVM looks (among other places) for a hello/HelloWorld.class
file. You need to keep that hello
structure when you copy files into the container.
The COPY
line should include the entire hello
directory. The RUN javac ...
line should use a relative path to the source file. Together this looks like
FROM openjdk:11-jdk-slim
WORKDIR /app
COPY hello/ hello/ # relative path on right-hand side
RUN javac hello/HelloWorld.java # relative path in filename
CMD ["java", "hello.HelloWorld"]
(For any larger project you may want a more substantial build system, like Maven or Gradle. For Java it's not uncommon to build the application outside Docker and only COPY
the jar file into the image; or if you're running the build in the Dockerfile, make sure to COPY
the entire src
tree in standard project layouts.)