В Java есть ли способ получить параметры командной строки, даже если main() их не сохранила?
-
21-08-2019 - |
Вопрос
У нас есть программа с main(), которая анализирует определенные CLP, но нигде их не сохраняет.Затем у меня есть свой собственный код подключаемого модуля, которому необходим доступ к оригинальным CLPS (чтобы я мог передавать больше параметров) для него.Однако я не могу изменить main()
Я увидел, что, по-видимому, есть способ сделать это на C#, Я ищу эквивалентное Java-решение в Linux.
Обновить:Очевидно, я в курсе того, как работает main().К сожалению, я не могу изменить существующее приложение или способ его вызова (за исключением CLPs).Я могу получить доступ только через изолированный код плагина.Мой вопрос заключается в том, есть ли способ получить командную строку (а не переменные среды с помощью -D), с помощью которой была вызвана JVM.
Решение
Помимо того, что вы каким-то образом выполняете это в main, я думаю, что единственным другим вариантом, который у вас есть, было бы перейти на уровень операционной системы и выполнить некоторые команды для получения аргументов.
В Linux аргументы строки cmd для запущенного процесса хранятся по адресу /процесс/пид/командная строка
Таким образом, чтобы получить их, вам нужно было бы найти идентификатор процесса.Смотрите здесь:
Как программа Java может получить свой собственный идентификатор процесса?
Затем, используя этот открытый /процесс/пид/командная строка и проанализируйте это.Формат этого файла и пример на c приведены здесь:
Возможно, было бы лучше обернуть эти два вызова в один сценарий оболочки, который вы вызываете из java.
Пожалуйста, обратите внимание, что это будет крайне непереносимо и немного халтурно.Но если нужно, обязательно...
Другие советы
Решение становится простым, как только вы понимаете, что основной метод Java - это просто еще один статический метод, который принимает массив строк в качестве аргумента.
Создайте новый класс, в котором хранятся CLP, затем вызовите старый класс.Позже вы сможете получить доступ к своим CLP, используя новый класс:
import NotToBeChangedMainClass;
public MyMainClass {
public static final String[] ARGUMENTS;
public static void main(String ... args) {
ARGUMENTS = args;
NotToBeChangedMainClass.main(args);
}
}
Наконец, измените любого внешнего вызывающего абонента (напримерлюбые пакетные файлы) использовать MyMainClass вместо NotToBeChangedMainClass.Если вы используете runable jars или что-то подобное, для этого требуется изменить соответствующий файл конфигурации.
Создайте свой собственный основной класс.Сохраните аргументы.Позвоните старому main
.
Это могло бы быть проще в использовании System.getProperty
и -Dkey=value
в командной строке (перед именем основного класса или -jar
).
В случае, если у вас нет выбора, вам абсолютно необходимо сохранить все существующие имена классов с их точным названием (как указано в вашем комментарии к моему предыдущему ответу), тогда вы должны использовать AspectJ .
Давайте рассмотрим, что у нас есть этот класс:
public class UnmodifyableClassWithMain {
public static void main(String[] args) {
System.out.println("In main");
new ClassUsingArgumentRegistry();
}
}
Во-первых, вам нужно что-то, что содержит аргументы командной строки.Для простоты я буду использовать простой класс со статическим полем:
public class ArgumentRegistry {
public static String[] ARGS;
}
Затем вам нужно определить Аспект, который перехватывает вызовы main и сохраняет аргументы.
public aspect StoreArgumentsOfMain {
/**
* This pointcut intercepts all calls to methods called main with a string array as
* argument.
*/
pointcut mainMethod(String[] arguments): execution(void main(String[])) && args(arguments);
/**
* Before the original main method gets called, store the arguments in the registry.
*/
before(String[] arguments): mainMethod(arguments) {
System.out.println("Storing arguments");
ArgumentRegistry.ARGS = arguments;
}
}
Чтобы попробовать это, я также создал ClassUsingArgumentRegistry:
public class ClassUsingArgumentRegistry {
public ClassUsingArgumentRegistry() {
System.out.println("Arguments: " + java.util.Arrays.toString(ArgumentRegistry.ARGS));
}
}
Вот и все.Если я включу ткачество AspectJ во время компиляции и запущу результат, используя "java UnmodifyableClassWithMain Foo Bar Baz", я получу следующий результат:
Storing arguments
In main
Arguments: [foo, bar, baz]
Обратите внимание, что это решение очень ограничено, поскольку Linux усекает командную строку, которую оно сохраняет.Поскольку командные строки Java часто имеют очень длинные пути к классам, это очень реальная проблема.
Вот Java-код, реализующий ответ, данный Pablojim.
package test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Test {
public static List<String> getLinuxCmdline(int maxBytesToRead) throws IOException {
List<String> result = new ArrayList<>();
String pid = new File("/proc/self").getCanonicalFile().getName();
File cmdlineFile = new File("/proc/" + pid + "/cmdline");
final int growBy = 1024;
try (FileInputStream is = new FileInputStream(cmdlineFile);) {
byte[] data = new byte[Math.min(growBy, maxBytesToRead)];
int totalRead = 0;
while (totalRead < maxBytesToRead) {
int read = is.read(data, totalRead, data.length - totalRead);
if (read > 0) {
totalRead += read;
if (data.length == totalRead) {
data = Arrays.copyOf(data, Math.min(data.length + growBy, maxBytesToRead));
}
} else {
break;
}
}
int start = 0;
int scan = 0;
while (scan < totalRead) {
if (data[scan] == 0) {
result.add(new String(Arrays.copyOfRange(data, start, scan)));
start = scan + 1;
}
scan++;
}
if (scan - start > 0) result.add(new String(Arrays.copyOfRange(data, start, scan))); }
return result;
}
public static void main(String[] args) throws IOException {
System.out.println(getLinuxCmdline(Integer.MAX_VALUE));
}
}
Запуск этого с аргументами "foo bar" в Eclipse дает это для меня:
[/usr/lib/jvm/java-8-oracle/bin/java, -Dfile.encoding=UTF-8, -classpath, /home/mab/workspace/test/bin, test.Test, foo, bar]