Question

Je travaille sur un programme écrit en Java qui, pour certaines actions , lance des programmes externes à l'aide des lignes de commande configurées par l'utilisateur. À l'heure actuelle, il utilise Runtime.exec() et ne conserve pas la référence Process (les programmes lancés sont soit un éditeur de texte ou utilitaire d'archivage, donc pas besoin pour le système in / out / flux err).

Il y a un problème mineur avec ce même si, en ce que lorsque les sorties du programme Java, il ne quitte pas vraiment jusqu'à ce que sont tous les programmes sont sortis lancés.

Je préfère grandement si les programmes lancés étaient totalement indépendants de la machine virtuelle Java qui les a lancés.

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

Quelqu'un sait comment faire exécuter le programme complètement lancé indépendamment de la machine virtuelle Java?


Modifier en réponse à un commentaire

Le code de lancement est la suivante. Le code peut lancer un éditeur positionné sur une ligne spécifique et la colonne, ou il peut lancer une visionneuse d'archives. valeurs indiquées dans la ligne de commande configuré sont traités comme ECMA-262 codé, et sont décodées et les citations dépouillés pour former le paramètre exec désiré.

Le lancement se produit 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 que lorsque le programme est lancé à partir de mon IDE. Je termine cette question, car le problème existe que dans mon environnement de développement; il est pas un problème dans la production . Dans le programme de test dans l'une des réponses, et d'autres tests que j'ai réalisé que je suis convaincu que ce n'est pas un problème qui sera vu par un utilisateur du programme sur toute plate-forme.

Était-ce utile?

La solution

Il peut être utile si vous postez une section d'essai du code minimal nécessaire pour reproduire le problème. Je l'ai testé le code suivant sous 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 ce qui suit sur Linux:

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

où test.sh ressemble:

#!/bin/bash
ping -i 20 localhost

ainsi que cela sur Linux:

java -jar JustForTesting.jar gedit

Et testé sous Windows:

java -jar JustForTesting.jar notepad.exe

Tous ces programmes ont lancé leurs destinées, mais l'application Java avait pas de problème sortant. Je les versions suivantes de machine virtuelle Java de Sun tel que rapporté par java -version:

  • Fenêtres: 1.6.0_13-b03
  • Linux: 1.6.0_10-b33

Je n'ai pas eu l'occasion de tester sur mon Mac encore. Peut-être il y a une interaction avec d'autres codes se produisant dans votre projet qui ne peut pas être clair. Vous pouvez essayer cette application de test et de voir quels sont les résultats.

Autres conseils

Il y a une relation parent-enfant entre vos processus et vous devez briser cela. Pour Windows, vous pouvez:

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

Pour Linux le processus semblent courir détaché de toute façon, pas nohup 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 est impossible de lui avec Runtime.exec dans une plate-forme de manière indépendante.

pour le système compatible POSIX:

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

Bien que cette question est fermée, j'ai quelques observations qui peuvent aider d'autres personnes confrontées à la même question.

Lorsque vous utilisez Runtime.getRuntime (). Exec () et vous ignorez la java.lang.Process poignée vous obtenez en retour (comme dans le code de l'affiche d'origine), il y a une chance que le processus lancé peut se bloquer.

J'ai fait face à ce problème dans un environnement Windows et tracé le problème aux flux stdout et stderr. Si l'application lancée est en train d'écrire à ces flux, et le tampon pour ces flux se remplit alors l'application lancée peut sembler se bloquer quand il essaie d'écrire sur les cours d'eau. Les solutions sont:

  1. Saisissez la poignée de processus et vider les cours d'eau en permanence - mais si vous voulez mettre fin à l'application java juste après le lancement du processus, alors ce n'est pas une solution réalisable
  2. Exécuter l'appel de processus comme 'cmd / c <>' (ce qui est seulement pour l'environnement Windows).
  3. Suffixe la commande de processus et de rediriger les courants de stdout et stderr à l'aide de NUL ' command> NUL 2> & 1 '

Vous voulez lancer le programme en arrière-plan, et il se séparer du parent. Je considère nohup (1) .

Je soupçonne que cela nécessiterait une fourchette de processus réel. En gros, 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 une fork () en Java pur. Ce que je voudrais faire 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 machine virtuelle Java ne peut toujours pas quitter, mais pas d'interface graphique et seule une empreinte mémoire limitée restent.

J'ai tout essayé mentionné ici, mais sans succès. parent principal processus Java ne peut pas quitter jusqu'à ce que la quitter de subthread même avec cmd / c démarrer et rediriger les 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ée. est le lien.

Une façon je peux penser est d'utiliser Runtime.addShutdownHook pour enregistrer un fil qui tue tous les processus (vous auriez besoin de conserver le processus des objets quelque part bien sûr).

Le crochet d'arrêt est appelé uniquement lorsque la machine virtuelle Java sort donc il devrait fonctionner correctement.

Un peu d'un hack, mais efficace.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top