I am using Java 13 preview features, but I can't find the option in jlink to add the "--enable-preview" flag to the java command inside of the launcher script jlink generates.
As you can see, the launcher script that is generated is
#!/bin/sh
JLINK_VM_OPTIONS=
DIR=`dirname $0`
$DIR/java $JLINK_VM_OPTIONS -m wla_server/net.saga.snes.dev.wlalanguageserver.Main $#
However, I don't know how to get the script to be generated with JLINK_VM_OPTIONS=--enable-preview using the jlink command.
My jlink command is
$JAVA_HOME/bin/jlink \
--module-path target/classes:target/dependency \
--add-modules ALL-MODULE-PATH \
--launcher launcher=wla_server/net.saga.snes.dev.wlalanguageserver.Main \
--output dist/linux \
--vm=server \
--compress 2
I've added the following sed command to my build script :
sed -i 's/JLINK_VM_OPTIONS=.*/JLINK_VM_OPTIONS=--enable-preview/' dist/linux/bin/launcher
This edits the launcher script:
#!/bin/sh
JLINK_VM_OPTIONS=--enable-preview
DIR=`dirname $0`
$DIR/java $JLINK_VM_OPTIONS -m wla_server/net.saga.snes.dev.wlalanguageserver.Main $#
Which works well enough.
Related
I have created a java application that uses the Selenium library for web scraping. The application works in the IDE (eclipse). Now I want to create a mac bundle using java packager. But the java packager produces and error:
Exception: java.lang.Exception: Error: Modules are not allowed in srcfiles: [../Released/run.jar].
Error: Bundler "Mac Application Image" (mac.app) failed to produce a bundle.
The script that runs the java packager looks like this:
#!/bin/bash
export JAVA_HOME="/Library/Java/JavaVirtualMachines/jdk-11.0.3.jdk/Contents/Home"
export PATH=$JAVA_HOME/bin:$PATH
./jpackager \
create-image \
-i ../Released \
-o ../Released \
-n "MyWineApp" \
-c mywineapp.gui.MyWineApp \
-j run.jar \
-v "1.0.0" \
--icon ../Images/wine.icns \
--add-modules java.base,java.desktop \
--jvm-args '--add-opens javafx.base/com.sun.javafx.reflect=ALL-UNNAMED'
I have used this script before successfully. The difference with this project is that this projects uses the Selenium library. What is the right way to use java packager in this case?
I'm using jlink to create a customized JRE to be used in docker
FROM gradle:7.5.1-jdk17-jammy As build
WORKDIR /build
COPY my-source my-source
RUN cd my-source && gradle clean build
RUN jlink \
--module-path /... \
--add-modules ... \
--output jre-custom \
--strip-debug \
--no-header-files \
--no-man-pages \
--compress 2
FROM alpine:latest
WORKDIR /deployment
COPY --from=build /build/jre-custom jre-custom/
COPY --from=build /build/my-source/build/libs/*.jar build/app.jar
# ERROR at line bellow: /bin/sh: jre-custom/bin/java: not found
CMD ["jre-custom/bin/java","-jar","build/app.jar"]
EXPOSE 3009
When I'm running jre-custom/bin/java -version in alpine image I've got:
/bin/sh: jre-custom/bin/java: not found
Is there anything in alpine image to be installed?
NOTE: I can run jre-custom/bin/java -version in first stage docker successfully.
I've got the solution changing the first stage image to alpine-based image as follow
# using image on alpine-based
FROM gradle:7.5.1-jdk17-alpine As build
WORKDIR /build
COPY my-source my-source
# install required packages
RUN apk update && apk add gcompat binutils
RUN gradle -p my-source clean build
RUN jlink \
--module-path /opt/java/openjdk/jmods \
--add-modules $(jdeps --ignore-missing-deps --print-module-deps --multi-release 17 my-source/build/libs/*.jar ) \
--output jre-custom \
--strip-debug \
--no-header-files \
--no-man-pages \
--compress 2
# reduce image size a little bit more (-4MB)
RUN strip -p --strip-unneeded jre-custom/lib/server/libjvm.so && \
find jre-custom -name '*.so' | xargs -i strip -p --strip-unneeded {}
FROM alpine:latest
WORKDIR /deployment
COPY --from=build /build/jre-custom jre-custom/
COPY --from=build /build/my-source/build/libs/*.jar build/app.jar
CMD ["jre-custom/bin/java","-jar","build/app.jar"]
EXPOSE 3009
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.
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.
I am working in a Java spring boot application, running as a docker container.
The main purose of this application was to execute python scripts.
So inside the docker container, I had to make available python environment. I added the python runtime with this code.
But seems this is very basic python verson and I can not make other important libraries available.
Like, I wanted to add 'asyncpg' library so that I can use connection pool.
But it is not letting me to add asyncpg library.
Below is the docker file.
Note: I have commented '#FROM python:3.6-alpine', if I make this open then java runtime will not be available which is 'FROM openjdk:8u191-jre-alpine3.8'
------- Docker file --------------
*FROM openjdk:8u191-jre-alpine3.8
#FROM python:3.6-alpine
## Install bash
RUN apk add --no-cache curl tar bash
ENV APP_HOME /opt/app
# Create directory structure
RUN mkdir -p ${APP_HOME}/logs
RUN mkdir -p ${APP_HOME}/config
RUN mkdir -p ${APP_HOME}/libs
RUN mkdir -p ${APP_HOME}/scripts
# Add supporting script
ADD start.sh ${APP_HOME}/start.sh
RUN chmod +x ${APP_HOME}/start.sh
ADD wait-for-it.sh ${APP_HOME}/wait-for-it.sh
RUN chmod +x ${APP_HOME}/wait-for-it.sh
ADD load-ext-packages.sh ${APP_HOME}/load-ext-packages.sh
RUN chmod +x ${APP_HOME}/load-ext-packages.sh
## Add Spring Boot runnable jar
ADD *.jar ${APP_HOME}/
WORKDIR ${APP_HOME}
VOLUME [ "${APP_HOME}/logs" ]
VOLUME [ "${APP_HOME}/config" ]
VOLUME [ "${APP_HOME}/libs" ]
VOLUME [ "${APP_HOME}/scripts" ]
# Install Python
RUN apk add build-base
RUN apk add --update gcc
RUN apk --update add gcc build-base freetype-dev libpng-dev openblas-dev
RUN apk add --no-cache python3 python3-dev libevent-dev && \
python3 -m ensurepip && \
rm -r /usr/lib/python*/ensurepip && \
pip3 install --upgrade pip setuptools && \
pip3 install wheel && \
pip3 install --no-cache-dir asyncpg && \
if [ ! -e /usr/bin/pip ]; then ln -s pip3 /usr/bin/pip ; fi && \
if [[ ! -e /usr/bin/python ]]; then ln -sf /usr/bin/python3 /usr/bin/python; fi && \
rm -r /root/.cache
CMD ["./start.sh"]*
I have got one solution .. that at the first line of the docker file, I will first decleare the 'FROM alpine:3.7', and then inside the docker file I will keep adding the required runtimes and the dependent libraries accordingly. This way we can add mutiple runtimes.
Below the working docker compose where I have commented out both the lines for openjdk and python runtime and added FROM alpine:3.7:
Also some trick to add asyncpg library to the python runtime. This way I can now add any dependencies.
I can now work on asyncpg.
-------- Docker file ----------------
*#FROM openjdk:8u191-jre-alpine3.8
#FROM python:3.6-alpine
FROM alpine:3.7
## Install bash
RUN apk add --no-cache curl tar bash
ENV APP_HOME /opt/app
# Create directory structure
RUN mkdir -p ${APP_HOME}/logs
RUN mkdir -p ${APP_HOME}/config
RUN mkdir -p ${APP_HOME}/libs
RUN mkdir -p ${APP_HOME}/scripts
# Add supporting script
ADD start.sh ${APP_HOME}/start.sh
RUN chmod +x ${APP_HOME}/start.sh
ADD wait-for-it.sh ${APP_HOME}/wait-for-it.sh
RUN chmod +x ${APP_HOME}/wait-for-it.sh
ADD load-ext-packages.sh ${APP_HOME}/load-ext-packages.sh
RUN chmod +x ${APP_HOME}/load-ext-packages.sh
## Add Spring Boot runnable jar
ADD *.jar ${APP_HOME}/
WORKDIR ${APP_HOME}
VOLUME [ "${APP_HOME}/logs" ]
VOLUME [ "${APP_HOME}/config" ]
VOLUME [ "${APP_HOME}/libs" ]
VOLUME [ "${APP_HOME}/scripts" ]
RUN apk add build-base --no-cache python3 python3-dev && \
python3 -m ensurepip && \
rm -r /usr/lib/python*/ensurepip && \
pip3 install --upgrade pip setuptools && \
pip3 install --no-cache-dir asyncpg && \
if [ ! -e /usr/bin/pip ]; then ln -s pip3 /usr/bin/pip ; fi && \
if [[ ! -e /usr/bin/python ]]; then ln -sf /usr/bin/python3 /usr/bin/python; fi && \
rm -r /root/.cache
RUN apk update \
&& apk upgrade \
&& apk add --no-cache bash \
&& apk add --no-cache --virtual=build-dependencies unzip \
&& apk add --no-cache curl \
&& apk add --no-cache openjdk8-jre
CMD ["./start.sh"]*
I have built a Docker image for my Java application - https://bitbucket.org/ijabz/songkongdocker/src/master/Dockerfile
The last line is
CMD /opt/songkong/songkongremote.sh
the (simplified) contents of songremote.sh are
#!/bin/sh
umask 000
./jre/bin/java-jar lib/songkong-6.9.jar -r
and it works fine.
However I have a customer who wants to run songkong with the -m option and a path
e.g
#!/bin/sh
umask 000
./jre/bin/java-jar lib/songkong-6.9.jar -m /users/music
So is there a way a single docker can allow for two different command to be run, or do I have to build another docker.
Either way how do I allow /users/music to be provided by user
The full contents of songkongremote are a bit more complicated, since more options have to be passed to Java
#!/bin/sh
umask 000
./jre/bin/java -XX:MaxRAMPercentage=60 -XX:MetaspaceSize=45 -Dcom.mchange.v2.log.MLog=com.mchange.v2.log.jdk14logging.Jdk14MLog -Dorg.jboss.logging.provider=jdk -Djava.util.logging.config.class=com.jthink.songkong.logging.StandardLogging -Dhttps.protocols=TLSv1.1,TLSv1.2 --add-opens java.base/java.lang=ALL-UNNAMED -jar lib/songkong-6.9.jar -r
Update
I follows Cascaders answer and it has done something (look at Execution Command)
but songkong.sh is acting as if no parameters have been passed at all (rather than -r option passed).
songkong.sh (renamed from songkongremote.sh) now contains
#!/bin/sh
umask 000
./jre/bin/java -XX:MaxRAMPercentage=60 -XX:MetaspaceSize=45 -Dcom.mchange.v2.log.MLog=com.mchange.v2.log.jdk14logging.Jdk14MLog -Dorg.jboss.logging.provider=jdk -Djava.util.logging.config.class=com.jthink.songkong.logging.StandardLogging -Dhttps.protocols=TLSv1.1,TLSv1.2 --add-opens java.base/java.lang=ALL-UNNAMED -jar lib/songkong-6.10.jar "$#"
and the end of Dockerfile is now
EXPOSE 4567
ENTRYPOINT ["/sbin/tini"]
# Config, License, Logs, Reports and Internal Database
VOLUME /songkong
# Music folder should be mounted here
VOLUME /music
WORKDIR /opt/songkong
ENTRYPOINT /opt/songkong/songkong.sh
CMD ["-r"]
I don't understand if okay that there are two entrypoints or the signifcance of the sbin/tini one
So is there a way a single docker can allow for two different command
to be run, or do I have to build another docker.
Yes, you can handle this using single Dockerfile base on command with help of entrypoint.
entrypoint.sh
#!/bin/sh
umask 000
if [ "$1" == "-m" ];then
echo "starting container with -m option"
./jre/bin/java-jar lib/songkong-6.9.jar "$#"
else
./jre/bin/java-jar lib/songkong-6.9.jar -r
fi
so if the docker run command look like this
docker run -it --rm myimage -m /users/music
it will execute first condition
But if [ "$1" == "-m" ] this will break if user pass like "-m /users/music" in double-quotes, as the condition only checking for first argument. you can adjust this accordingly.
So the Dockerfile will only contain entrypoint
ENTRYPOINT ["/opt/songkong/songkongremote.sh"]
Or you can CMD specify in Dockerfile
ENTRYPOINT ["/opt/songkong/songkongremote.sh"]
CMD ["-m", "/song/myfile]"
update:
You can not define two entrypoint per docker image, the first will be ignore.
Now to process to argument from CMD you should use array type syntax otherwise it will seems empty
ENTRYPOINT ["/opt/songkong/songkong.sh"]
CMD ["-r"]
You may try to use ENTRY point for the command, and CMD only for arguments
ENTRYPOINT ["/sbin/tini", "/opt/songkong/songkongremote.sh" ]
CMD [] # Arguments are passed here
You also need to handle arguments in your .sh file:
#!/bin/sh
umask 000
./jre/bin/java-jar lib/songkong-6.9.jar "$#"
You can switch to ENTRYPOINT instead of CMD. Your docker file will look like:
...
ENTRYPOINT [ "/opt/songkong/songkongremote.sh" ]
...
Your customers would then run your image as:
docker run img -r
or
docker run img -m /users/music
You can even alter your bash script to accept arguments and pass those arguments using the same method.
If you need to provide default arguments and the ability to override them on demand, the following approach will work:
Dockerfile:
...
ENTRYPOINT [ "/opt/songkong/songkongremote.sh" ]
CMD ["-r"]
...
Your modified script:
#!/bin/sh
umask 000
./jre/bin/java \
-XX:MaxRAMPercentage=60 \
-XX:MetaspaceSize=45 \
-Dcom.mchange.v2.log.MLog=com.mchange.v2.log.jdk14logging.Jdk14MLog \
-Dorg.jboss.logging.provider=jdk \
-Djava.util.logging.config.class=com.jthink.songkong.logging.StandardLogging \
-Dhttps.protocols=TLSv1.1,TLSv1.2 \
--add-opens java.base/java.lang=ALL-UNNAMED \
-jar lib/songkong-6.9.jar "$#"
Then, when customers run docker run img the CMD parameter will kick-in, but when they run docker run img -m /users/music the entrypoint command will be executed with the provided arguments.