IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

AWT/Swing Java Discussion :

[Swing] Exécution d'une commande système et récupération de la sortie


Sujet :

AWT/Swing Java

  1. #1
    Candidat au Club
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 7
    Points : 3
    Points
    3
    Par défaut [Swing] Exécution d'une commande système et récupération de la sortie
    Bonjour,

    Je débute depuis quelques jours sur Swing (via NetBeans) et je suis tombé sur un problème auquel je n'ai pas trouvé de réponse sur le web.

    Je suis en train de créer un gestionnaire de téléchargement utilisant wget pour gérer les transferts de fichiers. J'ai donc un wget.exe (pour l'instant je développe une version windows) que je lance en ligne de commande et qui se charge de télécharger les fichiers correspondant aux liens que je lui passe en ligne de commande.

    Mon interface Swing se lance donc avec un paramètre correspondant au chemin d'accès d'un fichier contenant toutes les URL des fichiers que je dois télécharger.
    J'arrive à lancer ma commande système exécutant wget mais je ne parviens pas à récupérer sa sortie standard pour réutiliser ces informations dans mon interface.

    Pour l'instant, je ne cherche qu'à récupérer la sortie de mon exécution et la balancer dans un JTextArea.

    Donc ma question est la suivante : comment récupérer en temps réel la sortie (standard ou d'erreur) d'une commande afin de modifier l'interface en fonction ?

    Merci d'avance.

  2. #2
    Expert confirmé
    Avatar de le y@m's
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2005
    Messages
    2 636
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Février 2005
    Messages : 2 636
    Points : 5 778
    Points
    5 778

  3. #3
    Candidat au Club
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 7
    Points : 3
    Points
    3
    Par défaut
    Oui tout ça pas de problème, c'est comme ça que j'arrive à lancer ma commande système mais ça n'explique pas comment récupérer la sortie standard tout en laissant le process s'exécuter.
    A moins que mon utilisation de getInputStream() soit erronée.

  4. #4
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,

    Citation Envoyé par RisWaaq
    A moins que mon utilisation de getInputStream() soit erronée.
    Fais voir ton code et on pourra t'en dire plus

    a++

  5. #5
    Candidat au Club
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 7
    Points : 3
    Points
    3
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
        public static void main(String args[]) {
            if (args.length == 0)
                return;
            final String path = args[0];
            java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    MainFrame mf = new MainFrame();
                    mf.setVisible(true);
     
                    String cmd = "cmd /C C:\\wget\\wget.exe -i " + path + " -Sc";
                    // -i "file" -> télécharge toutes les cibles des liens contenus dans "file"
                    // -S -> télécharge et affiche le header avant de dl le fichier
                    // -c -> continue le téléchargement du fichier s'il a déjà commencé'
     
                    try {
                        Runtime run = Runtime.getRuntime();
                        Process p = run.exec(cmd);
     
                        String currentLine = null;
                        BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
                       while ((currentLine = in.readLine()) != null) {
                            mf.jTextArea1.append(currentLine);
                        }
                        BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
                        while ((currentLine = err.readLine()) != null) {
                            mf.jTextArea1.append(currentLine);
                        }
                    } catch (Exception e) {
                        System.out.println("Execution error " + cmd + e.toString());
                    }
                }
            });
        }

  6. #6
    Expert confirmé
    Avatar de le y@m's
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2005
    Messages
    2 636
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Février 2005
    Messages : 2 636
    Points : 5 778
    Points
    5 778
    Par défaut
    Tu doit consommer tous les fluxs du Process dans des threads séparés (le sujet a déjà été abordé plusieurs fois sur le forum).

  7. #7
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    C'est normal que ton interface ne soit pas mise à jours : tu effectues toutes la lecture des flux dans l'EDT. Du coups le thread qui s'occupe de raffraichir et d'afficher la fenêtre se retrouve bloquer ton interface est bloquée...

    De plus le traitement des flux de sortie standard et d'erreur doivent être effectué dans deux threads séparés pour éviter des problèmes d'interblocages... ([edit] a moins d'utiliser la classe ProcessBuilder de Java 5.0 qui permet de regrouper les deux flux -- quel version de Java tu utilises ?)

    a++

  8. #8
    Candidat au Club
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 7
    Points : 3
    Points
    3
    Par défaut
    version 1.5.

    Supposons, pour commencer, que je ne m'occupe que d'un seul flux pour écarter ce problème de threads séparés, comment effectuer la lecture des flux hors de l'EDT (et d'ailleurs qu'est-ce que l'EDT ? je subodore que c'est la partie qui s'occupe de récupérer les événements, est-ce que je me trompe ?)

  9. #9
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par RisWaaq
    Supposons, pour commencer, que je ne m'occupe que d'un seul flux pour écarter ce problème de threads séparés,
    Pour cela, et puisque tu es sous Java 5.0, je te conseille d'utiliser ProcessBuilder qui permet de rediriger le flux d'erreur vers le flux standard. C'est plus pratique à gérer dans certain cas...

    Citation Envoyé par RisWaaq
    comment effectuer la lecture des flux hors de l'EDT (et d'ailleurs qu'est-ce que l'EDT ? je subodore que c'est la partie qui s'occupe de récupérer les événements, est-ce que je me trompe ?)
    En partie oui. L'EDT gère l'affichage des composants Swing/AWT, ainsi que leurs évenements. Il constitue en fait en une queue d'action à effectuer.
    Lorsque tu utilises invokeLater() tu rajoutes un traitement dans cette queue, qui sera traité dès que possible. Si ce traitement prend trop de temps, les autres tâches (passé par invokeLater ou par le système comme les evénément) ne pourront pas être effectué et ton interface sera figé.

    Toutes les interactions avec l'interface graphique doivent être effectué dans l'EDT, sauf s'il est explicitement indiquer dans la documentation que la méthode est thread-safe.


    Tu trouveras plus d'info là dessus dans le tutoriel le Gfx :
    Tutoriel : Threads et performance avec Swing par Romain Guy
    Pour éviter ces problèmes tu es obligé de passer par un autre thread.
    La meilleure solution consiste à utiliser la classe SwingWorker qui permet de gérer facilement des traitements liés aux interfaces graphiques en gérant elles-mêmes les différents threads. Cette classe ne sera intégré en standard que dans le prochaine version de Java, mais tu peux l'utiliser en ajoutant une librairie externe téléchargeable sur java.net : https://swingworker.dev.java.net/


    Par exemple dans ton cas cela pourrait donner le code suivant (en bleu ce qui est exécuté dans un thread en tache de fond, en rouge ce qui est exécuté dans l'EDT) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    class ExecWorker extends SwingWorker<Object,String> {
        
        /** Caractère de fin de ligne */
        private static final String NEW_LINE = System.getProperty("line.separator");
        /** Le textArea dans lequel sera affiché le texte. */
        private final JTextArea textArea;
        /** Le builder qui sera exécuté par cette classe */
        private final ProcessBuilder builder;
        
        
        public ExecWorker(JTextArea textArea, String... cmd) {
            this.textArea = textArea;
            // On crée le builder
            this.builder = new ProcessBuilder(cmd);
            // Et on redirige le flux d'erreur vers le flux standard
            this.builder.redirectErrorStream(true);
        }
         
        /*
         * La méthode doInBackground sera effectué dans un thread en tâche de fond.
         * Cela permet d'effectuer des traitements 'lourd' ou 'lent' sans bloquer
         * complètement l'affichage de l'interface graphique.
         * Cette méthode ne doit pas modifier directement l'interface graphique...
         */
        @Override
        protected Object doInBackground() throws Exception {
            // On redirige le flux d'erreur vers le flux standard,
            // afin de n'avoir qu'un flux a traiter (getErrorStream() devient inutile)
            builder.redirectErrorStream(true);
            // On démarre le processus (équivalent du Runtime.exec()
            Process p = builder.start();
            
            // On ferme le flux stdin du programme appellé (puisqu'on ne l'utilise pas)
            p.getOutputStream().close();
            
            // On lit le flux d'entrée :
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    p.getInputStream())); // Puisque tu es sous Windows, il te faudra rajouter le type de charset "cp850" au risque de te retrouver avec des caractères illisibles...
             try {
                String currentLine;
                while ((currentLine = in.readLine()) != null) {
                    // Pour chaque ligne lu, on appelle la méthode publish()
                    // Cette dernière se 'contente' de passer ses paramètres
                    // à la méthode process() ci dessous
                    publish(currentLine);
                }
            } finally {
                in.close();
            }
            
            // Enfin on attend la fin du process
            p.waitFor();
            return null;
        }
        
    /*
         * La méthode process() est appellé implicitement lorsque on appelle publish().
         * Elle est exécuté dans l'EDT et permet donc de modifier l'interface selon les données
         * reçu par la méthode doInBackground()...
         * Toutefois rien ne garantit que pour un appel à publish() il y ait un appel à process(),
         * ainsi pour 300 appel à publish() tu peux avoir 2 appel à process() avec 150 paramètres...
         * Ceci pour des raisons de performances...
         */
        @Override
        protected void process(String... lines) {
            // On se contente d'afficher toutes les lignes dans le TextArea
            for (String line : lines) {
                this.textArea.append(line);
                this.textArea.append(NEW_LINE);
            }
            // Puis on déplace le curseur tout en bas :
            this.textArea.setCaretPosition(this.textArea.getDocument().getLength());
        }
    }
    que tu appellerais de cette manière :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
       ExecWorker worker = new ExecWorker( mf.jTextArea1,
          "cmd", "/C", "C:\\wget\\wget.exe", "-i", path, "-Sc");
       worker.execute();
    a++

  10. #10
    Candidat au Club
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 7
    Points : 3
    Points
    3
    Par défaut
    Ca c'est de la réponse, merci beaucoup, je vais me pencher là-dessus.

    Cela dit, dans le cadre de mon application, je vais finalement gérer les transfers via une API Java plutôt que par un programme tiers, ça minimisera les choses à changer pour les différentes versions...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Exécuter une commande système avec perl
    Par Olivier Regnier dans le forum Langage
    Réponses: 12
    Dernier message: 08/04/2007, 17h41
  2. Réponses: 3
    Dernier message: 27/03/2007, 15h39
  3. Réponses: 2
    Dernier message: 05/01/2007, 17h57
  4. exécuter une commande système à partir de sqlplus?
    Par c_moi_c_moi dans le forum Oracle
    Réponses: 24
    Dernier message: 08/11/2005, 16h11
  5. [Système] Pb exécution d'une commande externe
    Par licorne dans le forum Langage
    Réponses: 9
    Dernier message: 19/10/2005, 17h34

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo