Статьи

(Re) Запусти меня!

 

В некоторых случаях вы хотели бы запустить процесс Java, идентичный текущему (или, по крайней мере, использовать ту же JVM с измененными параметрами). Некоторые конкретные случаи, когда это было бы полезно:

  • Автоматическая настройка параметров максимальной памяти (т. Е. У вас есть алгоритм для определения оптимального значения — например, 80% системной памяти — и ваша JVM не была запущена с этим конкретным значением)
  • Создание кластера процессов для высокой (er) -доступности (истинная HA подразумевает наличие нескольких физических узлов) или потому, что процессы играют разные роли (например, компоненты в MongoDB).
  • Демонизация текущего процесса (то есть фоновый процесс должен запускаться даже после завершения процесса запуска) — это очень часто modus-operandi для программ в системах * nix, где у вас есть процесс «управления» переднего плана и фоновый «демон» msgstr «процесс (не путать с потоками» демона «).

Это относительно просто — и может быть сделано в чистой Java — после того, как вы найдете правильные вызовы API:

List arguments = new ArrayList<>();
// the java executable
arguments
  .add(String.format("%s%sbin%sjava",
    System.getProperty("java.home"), File.separator,
    File.separator));
// pre-execuable arguments (like -D, -agent, etc)
arguments.addAll(ManagementFactory.getRuntimeMXBean()
  .getInputArguments());

String classPath = System.getProperty("java.class.path"), javaExecutable = System
  .getProperty("sun.java.command");
if (classPath.equals(javaExecutable)) {
 // was started with -jar
 arguments.add("-jar");
 arguments.add(javaExecutable);
} else {
 arguments.add("-classpath");
 arguments.add(classPath);
 arguments.add(javaExecutable);
}

// we might add additional arguments here which will be received by the
// launched program
// in its args[] paramater
arguments.add("runme");

// launch it!
new ProcessBuilder().command(arguments).start();

Некоторые пояснения по поводу кода:

  • Это во многом вдохновлено этим проектом
  • Мы предполагаем, что исполняемый файл Java имеет имя javaи находится bin/javaотносительно java.home. Мы используем File.separatorдля кода быть переносимым.
  • getInputArguments используется для получения конкретных аргументов, передаваемых в JVM (например -Xmx). Он не включает путь к классам.
  • Который взят из java.class.path
  • Наконец, есть один эвристический шаг: мы пытаемся определить, был ли мы запущены с использованием -jar myjar.jarсинтаксиса или MyMainClassсинтаксиса, и повторить его.

Это оно! После этого мы используем ProcessBuilder (который мы всегда должны отдавать предпочтению Runtime.exec, потому что он автоматически экранирует части командной строки для нас).

Заключительная мысль: если вы намереваетесь использовать этот метод для «демонизации» процесса (т. Е. Чтобы он продолжал работать после завершения родительского процесса), вам следует сделать две вещи:

  • Перенаправить стандартный ввод и вывод. По умолчанию они перенаправляются во временные буферы, и JVM, по-видимому, случайным образом завершает работу, когда эти буферы (каналы) заполняются.
  • Под Windows используйте javawвместо java. Это гарантирует, что процесс не будет привязан к консоли, с которой он был запущен (однако он все равно будет привязан к сеансу входа пользователя в систему и завершится, когда пользователь выйдет из системы — для более тяжелого решения загляните в сервис Java). Обертка ).

Это все на сегодня, надеюсь, вам понравилось, понравилось. Если вы запускаете код, и он не работает так, как было объявлено, дайте мне знать, чтобы я мог обновить его (особенно мне интересно, работает ли он с JVM не от Sun / Oracle). Приходите завтра за другой статьей!

Мета: этот пост является частью Java Advent Calendar и лицензирован под лицензией Creative Commons 3.0 Attribution . Если вам это нравится, пожалуйста, распространите информацию, поделившись, чирикать, FB, G + и так далее! Хотите написать для блога? Мы ищем участников для заполнения всех 24 слотов и хотели бы получить ваш вклад! Свяжитесь с Attila Balazs, чтобы внести свой вклад!