Hello, i have executable jar and i want execute something script on OutOfMemoryError and add pid as argument to script. My spring boot jar named outofmem.jar

I have next outofmem.conf file:

JAVA_OPTS=-Xmx200m -XX:OnOutOfMemoryError="/home/alexey/tmp/oom/oom_helper.sh %p" -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/alexey/tmp/oom/

If i start outofmem.jar i get next output:

[alexey@superman outofmem]$ ./outofmem.jar 
/srv/outofmem/outofmem.conf: line 1: -XX:OnOutOfMemoryError=/home/alexey/tmp/oom/oom_helper.sh %p: No such file or directory

If i change conf file to it:

JAVA_OPTS="-Xmx200m -XX:OnOutOfMemoryError="/home/alexey/tmp/oom/oom_helper.sh %p" -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/alexey/tmp/oom/"

i get next:

[alexey@superman outofmem]$ ./outofmem.jar 
/srv/outofmem/outofmem.conf: line 1: fg: no job control

I think problem in generated executable jar file. How i can pass %p to JVM? And yes, i know about pid file, but i want use %p

Comment From: philwebb

Have you tried escaping the quotes?

JAVA_OPTS="-Xmx200m -XX:OnOutOfMemoryError=\"/home/alexey/tmp/oom/oom_helper.sh %p\" -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/alexey/tmp/oom/"

Comment From: ghost

:) I tried it, but not described. Result is here: conf:

JAVA_OPTS="-Xmx200m -XX:OnOutOfMemoryError=\"/home/alexey/tmp/oom/oom_helper.sh %p\" -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/alexey/tmp/oom/"

Output:

[alexey@superman outofmem]$ ./outofmem.jar 
Error: Could not find or load main class %p"

Comment From: wilkinsona

Thanks for the report. This looks like a duplicate of #18540.

Comment From: ghost

@wilkinsona , Do you have some hotfix or recommendations?

Comment From: wilkinsona

I think so… This was surprisingly difficult to figure out due to bash's word splitting on spaces.

To take sufficient control over the treatment of the spaces, JAVA_OPTS needs to be defined as an array. Your conf file would then look like this:

JAVA_OPTS=("-Xmx200m" "-XX:OnOutOfMemoryError='home/alexey/tmp/oom/oom_helper.sh %p'" "-XX:+HeapDumpOnOutOfMemoryError" "-XX:HeapDumpPath=/home/alexey/tmp/oom/")

To cope with JAVA_OPTS being an array, you then need to use a custom launch script that's slightly modified from the default. The following is the diff of the change:

diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/resources/org/springframework/boot/loader/tools/launch.script b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/resources/org/springframework/boot/loader/tools/launch.script
index a7bbd78b09..6a90d02b20 100755
--- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/resources/org/springframework/boot/loader/tools/launch.script
+++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/resources/org/springframework/boot/loader/tools/launch.script
@@ -158,7 +158,7 @@ else
     exit 1
 fi

-arguments=(-Dsun.misc.URLClassPath.disableJarChecking=true $JAVA_OPTS -jar "$jarfile" $RUN_ARGS "$@")
+arguments=(-Dsun.misc.URLClassPath.disableJarChecking=true "${JAVA_OPTS[@]}" -jar "$jarfile" $RUN_ARGS "$@")

 # Action functions
 start() {

The [@] treats JAVA_OPTS as an array. It then needs to be wrapped in quotes so that each element of the array is handled as-is rather than further word-splitting on each element being performed.

You can learn how to provide a custom launch script in the reference documentation when using Gradle and Maven.

I'm no longer convinced that this is an exact duplicate of #18540 so I'm going to re-open it. I'm also unsure if we can make the change above to the default script. I'm worried about the switch to treating JAVA_OPTS as an array and how that'll affect users who have defined JAVA_OPTS but it is not an array. Hopefully the above is sufficient for your purposes in the meantime.

Comment From: ghost

Thank you so much @wilkinsona , you are Great! =)

Comment From: wilkinsona

To reduce the risk of regression, we should tackle #20335 before fixing this issue.

Comment From: wilkinsona

As I suspected, this change will break any conf file that is not expecting JAVA_OPTS to be treated as an array. Previously JAVA_OPTS=-Dserver.port=8081 -Dserver.servlet.context-path=/test would work. With the change to treat it as an array it needs to be JAVA_OPTS=(-Dserver.port=8081 -Dserver.servlet.context-path=/test) instead. Unfortunately, detecting if an array is a variable is far from easy in bash so, as far as I can tell, there's no good way for us to support both.

Requiring JAVA_OPTS to be an array definitely isn't something we can do in a maintenance release. It feels too much in a new feature release as well, particularly for something that's likely to be an edge-case. Let's see what the rest of the team thinks.

Comment From: wilkinsona

Using eval (as Tomcat does in catalina.sh, for example) for the direct execution case seems to address the problem without requiring JAVA_OPTS to be an array. I lack the necessary bash wizardy to make a similar change to the init.d cases, assuming that it's even possible.

Comment From: wilkinsona

We’re cleaning out the issue tracker and closing issues that we’ve not seen much demand to fix. Feel free to comment with additional justifications if you feel that this one should not have been closed.

Anyone affected by this issue should use a custom launch script that meets their needs.