Вопрос или проблема
Почему я наблюдаю разное поведение в этих 2 случаях? Я что-то упустил?
Команда:
new ProcessBuilder().directory(
Paths.get(System.getProperty("user.dir")).toFile())
.command("/usr/bin/java -Djava.library.path=/Users/myusername/myproject/lib/DynamoDBLocal_lib/ -jar /Users/myusername/myproject/lib/DynamoDBLocal.jar -sharedDb").start();
Стек вызовов:
Исключение в потоке "main" java.lang.RuntimeException: java.io.IOException: Не удается выполнить программу "/usr/bin/java -Djava.library.path=/Users/myusername/myproject/lib/DynamoDBLocal_lib/ -jar /Users/myusername/myproject/lib/DynamoDBLocal.jar -sharedDb" (в директории "/Users/myusername/myproject"): error=2, Нет такого файла или каталога
в com.comcast.tvx.app.xreserver.Main.exec(Main.java:47)
в com.comcast.tvx.app.xreserver.Main.main(Main.java:16)
Причина: java.io.IOException: Не удается выполнить программу "/usr/bin/java -Djava.library.path=/Users/myusername/myproject/lib/DynamoDBLocal_lib/ -jar /Users/myusername/myproject/lib/DynamoDBLocal.jar -sharedDb" (в директории "/Users/myusername/myproject"): error=2, Нет такого файла или каталога
в java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
в com.comcast.tvx.app.xreserver.Main.exec(Main.java:44)
... 1 больше
Причина: java.io.IOException: error=2, Нет такого файла или каталога
в java.lang.UNIXProcess.forkAndExec(Необходимый метод)
в java.lang.UNIXProcess.<init>(UNIXProcess.java:248)
в java.lang.ProcessImpl.start(ProcessImpl.java:134)
в java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
... 2 больше
Среда при сбое:
{PATH=/usr/bin:/bin:/usr/sbin:/sbin
JAVA_STARTED_ON_FIRST_THREAD_1074=1
SHELL=/bin/zsh
SECURITYSESSIONID=186a4
USER=myusername
JAVA_MAIN_CLASS_25188=com.comcast.tvx.app.xreserver.Main
APP_ICON_1074=../Resources/Eclipse.icns
COMMAND_MODE=unix2003
TMPDIR=/var/folders/t_/dlj2wfdj0bx2xl6mnnqmxyhj99pf4b/T/
SSH_AUTH_SOCK=/tmp/launch-Bhd1It/Listeners
DISPLAY=/tmp/launch-PuSx66/org.macosforge.xquartz:0
__CF_USER_TEXT_ENCODING=0x529B388B:0:0
Apple_PubSub_Socket_Render=/tmp/launch-hB7zpQ/Render
__CHECKFIX1436934=1
LOGNAME=myusername
HOME=/Users/myusername}
Больше информации
Когда я использую Runtime.exec(), среда выглядит идентично, но я не получаю ошибку:
Команда:
Runtime.getRuntime().exec("/usr/bin/java -Djava.library.path=/Users/myusername/myproject/lib/DynamoDBLocal_lib/ -jar /Users/myusername/myproject/lib/DynamoDBLocal.jar -sharedDb")
Среда в успешном случае:
{PATH=/usr/bin:/bin:/usr/sbin:/sbin
JAVA_STARTED_ON_FIRST_THREAD_1074=1
SHELL=/bin/zsh
JAVA_MAIN_CLASS_25360=com.comcast.tvx.app.xreserver.Main
SECURITYSESSIONID=186a4
USER=myusername
APP_ICON_1074=../Resources/Eclipse.icns
COMMAND_MODE=unix2003
TMPDIR=/var/folders/t_/dlj2wfdj0bx2xl6mnnqmxyhj99pf4b/T/
SSH_AUTH_SOCK=/tmp/launch-Bhd1It/Listeners
DISPLAY=/tmp/launch-PuSx66/org.macosforge.xquartz:0
__CF_USER_TEXT_ENCODING=0x529B388B:0:0
Apple_PubSub_Socket_Render=/tmp/launch-hB7zpQ/Render
__CHECKFIX1436934=1
LOGNAME=myusername
HOME=/Users/myusername}
Попробуйте изменить эту строку:
.command("/usr/bin/java -Djava.library.path=/Users/myusername/myproject/lib/DynamoDBLocal_lib/ -jar /Users/myusername/myproject/lib/DynamoDBLocal.jar -sharedDb").start();
на (прокрутите вправо, чтобы увидеть разницу):
.command("/usr/bin/java -Djava.library.path=/Users/myusername/myproject/lib/DynamoDBLocal_lib/ -jar /Users/myusername/myproject/lib/DynamoDBLocal.jar -sharedDb".split("\\s+")).start();
Объяснение: ввод в command()
должен быть массивом (или списком) строк, каждая из которых является другим аргументом (или “токеном”). Он не должен содержать пробелов!
Из документации:
команда, список строк, который указывает на файл внешней программы, который должен быть вызван, и его аргументы, если таковые имеются. Какой список строк представляет собой допустимую команду операционной системы, зависит от системы. Например, обычно каждый концептуальный аргумент является элементом этого списка, но существуют операционные системы, в которых программы ожидаются, чтобы они сами разбивали строку команды на токены – в такой системе реализация Java может потребовать, чтобы команды содержали ровно два элемента.
Ответ или решение
Проблема, с которой вы столкнулись, связана с тем, как обрабатываются аргументы командной строки в ProcessBuilder
по сравнению с Runtime.exec()
. Давайте разберем это подробнее.
Когда вы используете Runtime.exec()
, вы можете передавать строку команды целиком, и Runtime.exec()
сам разбивает эту строку на отдельные аргументы. Однако ProcessBuilder
ожидает, что вы предоставите список строк, где каждая строка представляет собой отдельный аргумент. Ваша текущая команда передана как одна строка, что и вызывает ошибку "No such file or directory".
Попробуйте изменить ваш вызов метода command()
следующим образом:
new ProcessBuilder()
.directory(Paths.get(System.getProperty("user.dir")).toFile())
.command("/usr/bin/java",
"-Djava.library.path=/Users/myusername/myproject/lib/DynamoDBLocal_lib/",
"-jar",
"/Users/myusername/myproject/lib/DynamoDBLocal.jar",
"-sharedDb")
.start();
Либо, если вы хотите оставить одну строку, используйте метод split()
для разбивания строки на отдельные аргументы:
.command("/usr/bin/java -Djava.library.path=/Users/myusername/myproject/lib/DynamoDBLocal_lib/ -jar /Users/myusername/myproject/lib/DynamoDBLocal.jar -sharedDb".split("\\s+"))
.start();
Обратите внимание, что split("\\s+")
разделяет строку по любому количеству пробелов, что позволяет вам избежать проблем с пробелами и сохранить корректное разбиение на аргументы.
Такой подход гарантирует, что ProcessBuilder
правильно интерпретирует каждый элемент как отдельный аргумент, что в свою очередь устранит возникшую ошибку и позволит вашему процессу запуститься без ошибок.
Резюме
- Используйте массив строк для аргументов в
ProcessBuilder
, чтобы избежать проблем с пробелами в командной строке. - Убедитесь, что каждый аргумент передается отдельно, чтобы
ProcessBuilder
мог корректно их обработать.
Эти изменения должны устранить вашу проблему и позволить успешно запустить DynamoDBLocal
.