Como eu inicio um processo completamente independente de um programa Java?

Eu estou trabalhando em um programa escrito em Java que, para algumas ações , lança programas externos usando linhas de comando configuradas pelo usuário. Atualmente, ele usa Runtime.exec() e não retém a referência Process (os programas lançados são um editor de texto ou um utilitário de archive, portanto, não há necessidade de streams in / out / err do sistema).

Há um pequeno problema nisso, pois quando o programa Java sai, ele não sai realmente até que todos os programas iniciados sejam encerrados.

Eu preferiria muito se os programas lançados fossem completamente independentes da JVM que os lançou.

O sistema operacional de destino é múltiplo, com o Windows, Linux e Mac sendo o mínimo, mas qualquer sistema GUI com uma JVM é realmente o que é desejado (daí a configuração do usuário das linhas de comando reais).

Alguém sabe como fazer o programa lançado executar completamente independente da JVM?


Editar em resposta a um comentário

O código de lançamento é o seguinte. O código pode iniciar um editor posicionado em uma linha e coluna específicas ou pode iniciar um visualizador de arquivos. Valores citados na linha de comando configurada são tratados como codificados ECMA-262 e são decodificados e as cotas removidas para formar o parâmetro exec desejado.

O lançamento ocorre no EDT.

 static Throwable launch(String cmd, File fil, int lin, int col) throws Throwable { String frs[][]={ { "$FILE$" ,fil.getAbsolutePath().replace('\\','/') }, { "$LINE$" ,(lin>0 ? Integer.toString(lin) : "") }, { "$COLUMN$",(col>0 ? Integer.toString(col) : "") }, }; String[] arr; // array of parsed tokens (exec(cmd) does not handle quoted values) cmd=TextUtil.replace(cmd,frs,true,"$$","$"); arr=(String[])ArrayUtil.removeNulls(TextUtil.stringComponents(cmd,' ',-1,true,true,true)); for(int xa=0; xa<arr.length; xa++) { if(TextUtil.isQuoted(arr[xa],true)) { arr[xa]=TextDecode.ecma262(TextUtil.stripQuotes(arr[xa])); } } log.println("Launching: "+cmd); Runtime.getRuntime().exec(arr); return null; } 

Isso parece estar acontecendo apenas quando o programa é iniciado a partir do meu IDE. Estou fechando essa questão, pois o problema existe apenas no meu ambiente de desenvolvimento; não é um problema na produção . A partir do programa de teste em uma das respostas, e outros testes que realizei estou satisfeito que não é um problema que será visto por qualquer usuário do programa em qualquer plataforma.

Pode ajudar se você publicar uma seção de teste de código mínimo necessário para reproduzir o problema. Eu testei o seguinte código no Windows e em um sistema Linux.

 public class Main { /** * @param args the command line arguments */ public static void main(String[] args) throws Exception { Runtime.getRuntime().exec(args[0]); } } 

E testado com o seguinte no Linux:

 java -jar JustForTesting.jar /home/monceaux/Desktop/__TMP/test.sh 

onde test.sh se parece com:

 #!/bin/bash ping -i 20 localhost 

bem como isso no Linux:

 java -jar JustForTesting.jar gedit 

E testado isso no Windows:

 java -jar JustForTesting.jar notepad.exe 

Todos eles lançaram seus programas pretendidos, mas o aplicativo Java não teve problemas para sair. Eu tenho as seguintes versões da JVM da Sun, conforme relatado pelo java -version :

  • Windows: 1.6.0_13-b03
  • Linux: 1.6.0_10-b33

Eu não tive a chance de testar no meu Mac ainda. Talvez haja alguma interação ocorrendo com outro código em seu projeto que pode não estar claro. Você pode querer experimentar este aplicativo de teste e ver quais são os resultados.

Existe uma relação pai-filho entre seus processos e você tem que quebrar isso. Para o Windows, você pode tentar:

 Runtime.getRuntime().exec("cmd /c start editor.exe"); 

Para Linux, o processo parece ser executado de qualquer maneira, sem necessidade de nenhum nohup. Eu tentei com gvim , midori e acroread .

 import java.io.IOException; public class Exec { public static void main(String[] args) { try { Runtime.getRuntime().exec("/usr/bin/acroread"); } catch (IOException e) { e.printStackTrace(); } System.out.println("Finished"); } } 

Eu acho que não é possível fazê-lo com Runtime.exec de forma independente de plataforma.

para sistema compatível com POSIX:

  Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "your command"}).waitFor(); 

Embora esta questão esteja encerrada, tenho algumas observações que podem ajudar outras pessoas a enfrentar problemas semelhantes.

Quando você usa Runtime.getRuntime (). Exec () e, em seguida, ignora o identificador java.lang.Process que recebe (como no código do pôster original), há uma chance de o processo iniciado ser interrompido.

Eu enfrentei esse problema no ambiente Windows e tracei o problema para os streams stdout e stderr. Se o aplicativo iniciado estiver gravando nesses streams e o buffer desses streams for preenchido, o aplicativo iniciado poderá parecer travar quando tentar gravar nos streams. As soluções são:

  1. Capture o identificador Process e esvazie os streams continuamente – mas se você quiser finalizar o aplicativo java logo após o lançamento do processo, essa não é uma solução viável
  2. Execute a chamada de processo como ‘cmd / c <>‘ (isso é somente para o ambiente Windows).
  3. Sufixe o comando process e redirecione os streams stdout e stderr para nul usando ‘ command> nul 2> & 1

Você deseja iniciar o programa em segundo plano e separá-lo do pai. Eu consideraria nohup (1) .

Eu suspeito que isso exigiria uma bifurcação de processo real. Basicamente, o equivalente C do que você quer é:

 pid_t id = fork(); if(id == 0) system(command_line); 

O problema é que você não pode fazer um fork () no Java puro. O que eu faria é:

 Thread t = new Thread(new Runnable() { public void run() { try { Runtime.getRuntime().exec(command); } catch(IOException e) { // Handle error. e.printStackTrace(); } } }); t.start(); 

Dessa forma, a JVM ainda não sairá, mas nenhuma GUI e apenas uma memory limitada permanecerá.

Uma maneira de pensar é usar Runtime.addShutdownHook para registrar um thread que elimine todos os processos (você precisaria reter os objects de processo em algum lugar, é claro).

O gancho de desligamento é chamado apenas quando a JVM sai, portanto, deve funcionar bem.

Um pouco de truque, mas eficaz.