Home > Net >  Running java by arguments in Docker
Running java by arguments in Docker

Time:12-03

Let's say we have the following Dockerfile for the purpose of creating a java image and compiling two scripts.

FROM openjdk:latest
COPY src JavaDocker
WORKDIR JavaDocker
RUN mkdir -p bin
RUN javac -d bin ./com/myapp/HelloWorld1.java
RUN javac -d bin ./com/myapp/HelloWorld2.java
WORKDIR bin
ENTRYPOINT java

How can I run any of these two scripts that have been compiled?

I'm using the command: docker run myapp-image "com.myapp.Server"

And I get:

Usage: java [options] <mainclass> [args...]
           (to execute a class)
   or  java [options] -jar <jarfile> [args...]
           (to execute a jar file)
   or  java [options] -m <module>[/<mainclass>] [args...]
       java [options] --module <module>[/<mainclass>] [args...]
           (to execute the main class in a module)
   or  java [options] <sourcefile> [args]
           (to execute a single source-file program)

 Arguments following the main class, source file, -jar <jarfile>,
 -m or --module <module>/<mainclass> are passed as the arguments to
 main class.

CodePudding user response:

For your example you can use an entrypoint and set the parameter via an environment variable.

RUN javac -d bin ./com/myapp/HelloWorld2.java
WORKDIR bin
ENV NAME="com.myapp.HelloWorld2"
ENTRYPOINT ["java", "-c", "$NAME"]

You can run using

docker run -e NAME="com.myapp.HelloWorld2" myapp-image

CodePudding user response:

I'd suggest building a separate image per application; that can help clarify what the image is supposed to do. I also generally recommend using CMD over ENTRYPOINT.

So a Dockerfile that runs only the first application could look like:

FROM openjdk:latest

# Prefer an absolute path for clarity.
WORKDIR /JavaDocker

# Set up the Java class path.
RUN mkdir bin
ENV CLASSPATH=/JavaDocker/bin

# Use a relative path as the target, to avoid repeating it.
# (If you change the source code, repeating `docker build` will
# skip everything before here.)
COPY src .

# Compile the application.
RUN javac -d bin ./com/myapp/HelloWorld1.java

# Set the main container command.
CMD ["java", "com.myapp.HelloWorld1"]

What if you do have an image that contains multiple applications? If you use CMD here, it's very easy to provide an alternate command when you run the image:

docker run myapp-image \
  java com.myapp.HelloWorld2

# Wait, what's actually in this image?
docker run --rm myapp-image \
  ls -l bin/com/myapp

I generally recommend reserving ENTRYPOINT for a wrapper script that does some first-time setup, then runs exec "$@" to run a normal CMD. There's an alternate pattern of giving a complete command in ENTRYPOINT, and using CMD to provide its arguments. In both of these cases ENTRYPOINT needs to be JSON-array syntax, not shell syntax.

ENTRYPOINT ["java", "com.myapp.HelloWorld1"] # <-- JSON-array syntax
CMD ["-argument", "to-program-1"]
docker run myapp-image \
  -argument=different -options

but it's harder to make that image do something else

docker run \
  --entrypoint ls \ # <-- first word of the command is before the image name
  myapp-image \
  -l bin/com/myapp  # <-- and the rest after

docker run \
  --entrypoint java \
  myapp-image \
  com.myapp.HelloWorld2

Your original Dockerfile will probably work if you change the ENTRYPOINT line from shell to JSON-array syntax; using shell syntax will cause the CMD part to be ignored (including a command passed after the docker run image-name). You might find it easier to make one complete application invocation be the default and include the java command if you need to run the other.

  • Related