Compile a java program, and have it create a persistent file

I’m trying to learn how to work with Docker.

I want in a container, to compile a small java program, CreateFile.java , and then run it, and have it create a file, and have that file persist on the host.

I can’t get it to work.

If I have Dockerfile:

FROM adoptopenjdk/openjdk14
WORKDIR /family
ADD CreateFile.java /family/CreateFile.java
RUN ["javac", "CreateFile.java"]
ENTRYPOINT ["java", "CreateFile"]

and the commands to build and run:

docker build -t createfile:v0 .
docker run -v C:/Users/paul/paul/java/src/helloworld-3/family:/family createfile:v0

then the “docker run” command produces output:

Error: Could not find or load main class CreateFile
Caused by: java.lang.ClassNotFoundException: CreateFile

I tried all sorts of variations:

  • replace ADD with COPY
  • replace ENTRYPOINT with a RUN command
  • change the order of the ADD and WORKDIR line
    This is just to show that I find it hard to understand exactly what these commands do.

I’m in dire need of some help.

Kind regards,
Paul van Bemmelen.

I forgot to show the java code (although the exact code may be irrelevant) :

import java.io.File;
import java.io.IOException;

public class CreateFile {
  public static void main(String[] args) {

    try {
      File file = new File("mama.txt");
      if (file.createNewFile()) {
        System.out.println("File created: " + file.getName());
      } else {
        System.out.println("File already exists.");
      }
    } catch (IOException e) {
      System.out.println("An error occurred.");
      e.printStackTrace();
    }
  }
}

1 Like

You’re compiling your source code into the WORKDIR, which is set to /family. But when running your image, you’re using a -v bind mount to change the location of that container path /family to be a folder on your local disk. And that hides the original /family within the image, into which you compiled your code earlier.

Running just docker run createfile:v0 will work, but then the output is stored in your container, not on your local disk.

One solution would be to use File file = new File("/family/mama.txt") to explicitly store the output in /family, and use something like WORKDIR /app along with ADD CreateFile.java . (where the dot . is relative to WORKDIR, so really just /app) in your Dockerfile. That uses a different path for your application, and now a bind mount for /family will no longer hide your compiled code.

Your suggestion works, Arjan. Many thanks!
Makes all the difference after all my earlier failed attempts.

I now have three files in directory C:/Users/paul/paul/java/src/helloworld-3 :
(I’m using Cygwin bash for the doit.sh script)

CreateFile.java :

import java.io.File;
import java.io.IOException;

public class CreateFile {
  public static void main(String[] args) {

    try {
      File file = new File("/family/mama.txt");
      if (file.createNewFile()) {
        System.out.println("File created: " + file.getName());
      } else {
        System.out.println("File already exists.");
      }
    } catch (IOException e) {
      System.out.println("An error occurred.");
      e.printStackTrace();
    }
  }
}

Dockerfile :

FROM adoptopenjdk/openjdk14
WORKDIR /app
ADD CreateFile.java .
RUN ["javac", "CreateFile.java"]
ENTRYPOINT ["java", "CreateFile"]

doit.sh :

#!/bin/sh
set -x
docker build -t createfile:v0 .
docker run -v C:/Users/paul/paul/java/src/helloworld-3/family:/family createfile:v0

And when I run ./doit.sh it nicely creates mama.txt in directory C:/Users/paul/paul/java/src/helloworld-3/family on the host.

1 Like

Asides: RUN really only affects building the image, and is not used when running the image as a container. Maybe you meant CMD? I find this overview of how CMD and ENTRYPOINT work together useful: Understand how CMD and ENTRYPOINT interact. And finally, I just noticed there is a language-specific Getting started with Java.

For the ENTRYPOINT, CMD and SHELL I also have a project with multiple examples. 11 exactly. It just shows how these instructions affect the final command running inside the container:

There is a link to a Youtube video too, where I show how the examples work, but I speak in Hungarian so you probably just want to watch it.

1 Like

Thanks Arjan en Ákos ,

From the link you sent, Arjan, I see that one really needs to read the documentation to understand how CMD and ENTRYPOINT interact.
The dockerfiles from you, Ákos, I may try them, but I’m a bit reluctant to use the shell script test.sh, cause there’s all sort of stuff there of which I don’t know what it’s doing. Maybe you can tell me the build and run command for running Dockerfile.v1 , so that I issue those commands by hand.

There is really nothing misterious in the build process.

docker build --force-rm -f Dockerfile.v1 -t localhost/command:v1

--force-rm is optional. I used it so the failed builds are not leaving build containers behind.
But the build is just the first step. How you run the container is also important. The test.sh doesn’t do anything more then running containers named like “localhost/command:TAG” and executing echo inside the container. It just shows multiple ways and adds colors and formattings. But you don’t have to run it. Just watch the video (you can even mute it). It will show you every generated command so you can run just docker commands. The scripts was made for me mainly.

But this is an other topic. If you really wan to discuss it more, I suggest you create a new topic and I can explain it in more detail if you need it.