Jest to raczej rzadka sytuacja, ale może zdarzyć się, że potrzebujecie do Javy przekazać system property, w którym jest spacja. Niestety, może być z tym kłopot. Czasem na drodze stoją Dockery, zmienne środowiskowe i shellowe skrypty uruchamiające. Na wypadek tak niekorzystnych warunków zaprezentuję opcję @
, która pozwala uciec od shellowych zawiłości i wczytać opcje Javy z pliku.
Podstawy
Jeśli mamy system property myopts
, to wartość opt1
przekażemy tak:
java -Dmyopts=opt1 -jar myapp.jar
Przekazanie wartości ze spacją w środku (opt1 opt2
) w korzystnych warunkach jest proste, można to zrobić na wiele sposobów. Poniższe 3 instrukcje działają i mają ten sam efekt:
java -Dmyopts='opt1 opt2' -jar myapp.jar
java -Dmyopts="opt1 opt2" -jar myapp.jar
java "-Dmyopts=opt1 opt2" -jar myapp.jar
Skrypt shellowy po drodze
Komplikacja pojawia się, gdy nie wołamy Javy bezpośrednio. Autorzy aplikacji dostarczają swój zawiły shellowy skrypt startowy. Do tego ktoś wymyślił, żeby całość zamknąć w obrazie dockerowym, przez co nie możemy tego skryptu łatwo modyfikować.
Skrypt w uproszczeniu będzie robił pewnie coś podobnego do:
#!/bin/env bash java $JAVA_OPTS -jar myapp.jar
Skrypt dobrze działa wołany bez spacji:
JAVA_OPTS="-Dmyopts=opt1" ./starter.sh
Ale wywali się wołany tak:
JAVA_OPTS="-Dmyopts='opt1 opt2'" ./starter.sh
Error: Could not find or load main class opt2'
Caused by: java.lang.ClassNotFoundException: opt2'
Sytuacja zupełnie się zmieni, gdy w skrypcie autorzy zastosowali dobrą zasadę skryptów shellowych, by zmienne otaczać cudzysłowami (ShellCheck), czyli w skrypcie jest java "$JAVA_OPTS"
i cała reszta tak samo. Niby drobna zmiana, ale zachowanie zupełnie inne. Wołamy wtedy:
JAVA_OPTS="-Dmyopts='opt1 opt2' -Dfoo=bar" ./starter.sh
a z opcjami dzieją się dziwne rzeczy. Wystarczy zmienić myopts
na file.encoding
, by zobaczyć, że różne system properties zostały zmieszane ze sobą, z dodatkiem pojedynczych cudzysłowów:
Caused by: java.nio.charset.IllegalCharsetNameException: 'opt1 opt2' -Dfoo=bar
Możecie się doktoryzować z Basha. Zakuwać scenariusze na wypadek skryptów, które otaczają zmienne cudzysłowami, oraz zupełnie inne na wypadek skryptów, które nie otaczają. Jeśli tak, to na StackOverflow jest pytanie passing Jvm properties (via -D) that contain spaces, miłego czytania. Możecie też podumać nad błędami w parserze opcji zastosowanym w poleceniu java
, dyskusja w JENKINS-57271.
Jeśli jednak wolicie skupić się na stronie praktycznej, mam prostą poradę działającą niezależnie od tego, co wymyślili sobie autorzy skryptu kolejnej aplikacji.
java @plik
Przez @
wczytujemy opcje z pliku:
java @my.vmoptions -jar myapp.jar
W pliku podajemy system properties. Działają cudzysłowy. Możemy wszystkie opcje umieścić w jednej linijce albo rozdzielić każdą na osobną linię:
-Dmyopts='opt1 opt2'
-Dfoo=bar
-XshowSettings:properties
Przekazywanie przez @
jest dostępne w Javie 11 i nowszych.