web-dev-qa-db-fra.com

Comment lancer un processus complètement indépendant à partir d'un programme Java?

Je travaille sur un programme écrit en Java qui, pour certaines actions , lance des programmes externes en utilisant la commande configurée par l'utilisateur Actuellement, il utilise Runtime.exec() et ne conserve pas la référence Process (les programmes lancés sont soit un éditeur de texte soit un utilitaire d'archivage, donc pas besoin de flux d'entrée/sortie/erron du système) .

Il y a cependant un problème mineur à cela, en ce que lorsque le programme Java se ferme, il ne se ferme pas vraiment jusqu'à ce que tous les programmes lancés soient fermés.

Je préférerais grandement que les programmes lancés soient complètement indépendants de la JVM qui les a lancés.

Le système d'exploitation cible est multiple, Windows, Linux et Mac étant le minimum, mais tout système d'interface graphique avec une machine virtuelle Java est vraiment ce qui est souhaité (d'où la configurabilité par l'utilisateur des lignes de commande réelles).

Quelqu'un sait-il comment exécuter le programme lancé de manière complètement indépendante de la JVM?


Modifier en réponse à un commentaire

Le code de lancement est le suivant. Le code peut lancer un éditeur positionné sur une ligne et une colonne spécifiques, ou il peut lancer une visionneuse d'archives. Les valeurs citées dans la ligne de commande configurée sont traitées comme codées ECMA-262 et sont décodées et les guillemets supprimés pour former le paramètre d'exécution souhaité.

Le lancement a lieu sur l'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;
    }

Cela semble se produire uniquement lorsque le programme est lancé à partir de mon IDE. Je ferme cette question car le problème n'existe que dans mon environnement de développement; ce n'est pas un problème en production. Du programme de test dans l'une des réponses, et des tests supplémentaires que j'ai effectués, je suis convaincu que ce n'est pas un problème qui sera vu par n'importe quel utilisateur du programme sur n'importe quelle plate-forme.

49
Lawrence Dol

Cela peut être utile si vous publiez une section de test du code minimal nécessaire pour reproduire le problème. J'ai testé le code suivant sur Windows et un système Linux.

public class Main {

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

Et testé avec les éléments suivants sur Linux:

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

où test.sh ressemble:

#!/bin/bash
ping -i 20 localhost

ainsi que ceci sous Linux:

Java -jar JustForTesting.jar gedit

Et testé cela sur Windows:

Java -jar JustForTesting.jar notepad.exe

Tous ces programmes ont lancé leurs programmes prévus, mais l'application Java n'a eu aucun problème lors de la fermeture. J'ai les versions suivantes de la machine virtuelle Java de Sun, telles que rapportées par Java -version:

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

Je n'ai pas encore eu l'occasion de tester sur mon Mac. Il se peut qu'une interaction avec un autre code de votre projet ne soit pas claire. Vous voudrez peut-être essayer cette application de test et voir quels sont les résultats.

18
monceaux

Il existe une relation parent-enfant entre vos processus et vous devez la rompre. Pour Windows, vous pouvez essayer:

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

Pour Linux, le processus semble fonctionner de toute façon, aucun Nohup n'est nécessaire. Je l'ai essayé avec gvim, midori et 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");
    }
}

Je pense qu'il n'est pas possible de le faire avec Runtime.exec d'une manière indépendante de la plateforme.

pour système compatible POSIX:

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

Bien que cette question soit close, j'ai quelques observations qui peuvent aider d'autres personnes confrontées à un problème similaire.

Lorsque vous utilisez Runtime.getRuntime (). Exec () et que vous ignorez le handle Java.lang.Process que vous récupérez (comme dans le code de l'affiche d'origine), il y a une chance que le processus lancé se bloque.

J'ai rencontré ce problème dans l'environnement Windows et j'ai retracé le problème aux flux stdout et stderr. Si l'application lancée écrit dans ces flux et que le tampon de ces flux se remplit, l'application lancée peut sembler se bloquer lorsqu'elle essaie d'écrire dans les flux. Les solutions sont:

  1. Capturez le descripteur de processus et videz continuellement les flux - mais si vous souhaitez mettre fin à l'application Java juste après le lancement du processus, ce n'est pas une solution réalisable
  2. Exécutez l'appel de processus en tant que "cmd/c <>" (ceci est uniquement pour l'environnement Windows).
  3. Suffixez la commande de processus et redirigez les flux stdout et stderr vers nul en utilisant 'commande> nul 2> & 1'
22
Manvendra Gupta

Vous souhaitez lancer le programme en arrière-plan et le séparer du parent. Je considérerais Nohup (1) .

1
Charlie Martin

Je soupçonne que cela nécessiterait une fourchette de processus réelle. Fondamentalement, l'équivalent C de ce que vous voulez est:

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

Le problème est que vous ne pouvez pas faire un fork () en Java pur. Ce que je ferais c'est:

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

De cette façon, la JVM ne se fermera toujours pas, mais aucune interface graphique et seulement une empreinte mémoire limitée resteront.

0
Matthew Flaschen

J'ai essayé tout ce qui est mentionné ici mais sans succès. Parent principal Java ne peut pas quitter tant que la sortie du sous-thread, même avec le démarrage de cmd/c et la redirection des flux tu nul.

Une seule solution fiable pour moi est la suivante:

try {
    Runtime.getRuntime().exec("psexec -i cmd /c start cmd.cmd");
}
catch (Exception e) {
    // handle it
}

Je sais que ce n'est pas clair, mais ce petit utilitaire de SysInternals est très utile et éprouvé. Ici est le lien.

0
Mroj