ProcessBuilder – Нет такого файла или каталога (но Runtime.exec() работает)

Вопрос или проблема

Почему я наблюдаю разное поведение в этих 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.

Оцените материал
Добавить комментарий

Капча загружается...