2011-06-20 6 views
5

Tout fonctionne bien sur la ligne de commande, mais quand je traduis ce que je veux en Java, le processus de réception ne reçoit jamais rien sur stdin.Comment écrire en Java à stdin de ssh?

Voici ce que j'ai:

private void deployWarFile(File warFile, String instanceId) throws IOException, InterruptedException { 
    Runtime runtime = Runtime.getRuntime(); 
    // FIXME(nyap): Use Jsch. 
    Process deployWarFile = runtime.exec(new String[]{ 
      "ssh", 
      "gateway", 
      "/path/to/count-the-bytes"}); 

    OutputStream deployWarFileStdin = deployWarFile.getOutputStream(); 
    InputStream deployWarFileStdout = new BufferedInputStream(deployWarFile.getInputStream()); 
    InputStream warFileInputStream = new FileInputStream(warFile); 

    IOUtils.copy(warFileInputStream, deployWarFileStdin); 
    IOUtils.copy(deployWarFileStdout, System.out); 

    warFileInputStream.close(); 
    deployWarFileStdout.close(); 
    deployWarFileStdin.close(); 

    int status = deployWarFile.waitFor(); 
    System.out.println("************ Deployed with status " + status + " file handles. ************"); 
} 

Le script 'count-the-octets' est tout simplement:

#!/bin/bash 

echo "************ counting stdin bytes ************" 
wc -c 
echo "************ counted stdin bytes ************" 

La sortie indique que la fonction est suspendue à la ligne 'wc -c' - il n'atteint jamais la ligne 'counted stdin bytes'.

Que se passe-t-il? Serait-il utile d'utiliser Jsch?

+0

Lorsque vous utilisez IOCopy, quelle valeur est retournée? – Snicolas

+0

J'ai changé le code pour utiliser copyLarge(); il a renvoyé 30054046. –

Répondre

4

Vous pouvez essayer de fermer le flux de sortie avant d'attendre le retour de wc -c.

IOUtils.copy(warFileInputStream, deployWarFileStdin); 
deployWarFileStdin.close(); 
IOUtils.copy(deployWarFileStdout, System.out); 

warFileInputStream.close(); 
deployWarFileStdout.close(); 
+0

J'avais ajouté la lecture de stdout afin de déboguer pourquoi le manuscrit original (que ssh est encore à une autre machine) ne fonctionnait pas. Après avoir fermé stdin avant de lire depuis stdout, la fonction semble fonctionner (même si cela fonctionnait parfois avant). La lecture de stdout force-t-elle le script à finir ce qu'il fait?Qu'est-ce qui est différent entre ça et l'appel de waitFor()? –

+0

@Noel: Le problème ici est la combinaison de 'copy' et' wc': 'wc' commencera seulement à sortir quelque chose si l'entrée est fermée, et' copy' attendra jusqu'à ce qu'il y ait une sortie du script (et le fin de celui-ci). Vous pouvez aussi mettre 'copy' et fermer un thread séparé. –

0

Vous obtenez probablement une erreur, mais le processus se bloque parce que vous ne lisez pas le error stream. Extrait du processus JavaDoc

Tout son io standard (stdin, stdout, stderr) opérations seront redirigées vers le processus parent par trois flux (Process.getOutputStream(), Process.getInputStream(), Process. getErrorStream()). Le processus parent utilise ces flux pour alimenter les entrées et obtenir des sorties du sous-processus. Étant donné que certaines plates-formes natives fournissent uniquement une taille de mémoire tampon limitée pour les flux d'entrée et de sortie standard, l'échec de l'écriture rapide du flux d'entrée ou de la lecture du flux de sortie du sous-processus risque de provoquer le blocage.

Vous devez donc tous les lire. L'utilisation du ProcessBuilder est probablement plus facile

+0

Bon conseil, mais dans ce cas, aucune erreur n'a été générée. –

1

L'utilisation de Jsch serait-elle utile?

L'utilisation JSch serait seulement utile si vous utilisez les méthodes au lieu de setInputStream() et setOutputStream() le canal de la méthode IOUtils.copy, car ils gèrent la copie sur un thread séparé.

ChannelExec deployWarFile = (ChannelExec)session.openChannel("exec"); 

deployWarFile.setCommand("/path/to/count-the-bytes"); 

deployWarFile.setOutputStream(System.out); 
deployWarFile.setInputStream(new BufferedInputStream(new FileInputStream(warFile))); 

deployWarFile.connect(); 

(Vous avez en quelque sorte d'attendre jusqu'à ce que l'autre côté ferme le canal.)

Si vous simplement remplacé le Runtime.exec avec l'ouverture d'un ChannelExec (et à partir après l'obtention des cours d'eau), le problème être tout à fait la même, et pourrait être résolu par la même solution mentionnée par antlersoft, c.-à-fermeture de l'entrée avant de lire la sortie:

ChannelExec deployWarFile = (ChannelExec)session.openChannel("exec"); 

deployWarFile.setCommand("/path/to/count-the-bytes"); 

OutputStream deployWarFileStdin = deployWarFile.getOutputStream(); 
InputStream deployWarFileStdout = new BufferedInputStream(deployWarFile.getInputStream()); 
InputStream warFileInputStream = new FileInputStream(warFile); 

deployWarFile.connect(); 

IOUtils.copy(warFileInputStream, deployWarFileStdin); 
deployWarFileStdin.close(); 
warFileInputStream.close(); 

IOUtils.copy(deployWarFileStdout, System.out); 
deployWarFileStdout.close(); 

(Bien sûr, si vous avez sortie plus, vous aurez envie de faire entrée et sortie en para llel, ou simplement utiliser la première méthode.)