Erro ao tratar InputStream de Process

6 respostas
Luiz_Henrique_Coura

Falae galera!

Criei uma classe "ProcessInstance" onde eu encapsulei algumas funcionalidades para se trabalhar com Process.
O resultado tá logo abaixo:

public class ProcessInstance {
    private Process process;
    private PrintStream processInput;
    private InputStream processOutput;
    private BufferedReader bufferedReader;
    
    private String processName;
    
    public ProcessInstance( String processName, Process process ) {
        setProcessName( processName );
        setProcess( process );
    }
    
    public Process getProcess() {
        return process;
    }
    
    public void setProcess(Process process) {
        this.process = process;
        setStreams();
    }
    
    protected void setStreams() {
        this.processInput = new PrintStream( getProcess().getOutputStream() );
        this.processOutput = getProcess().getInputStream();
        
        bufferedReader = new BufferedReader( new InputStreamReader( processOutput ) );
    }
    
    public void input( String message ) {
        processInput.println( message );
        processInput.flush();
    }
    
    public void output() {
        try {
            while(!bufferedReader.ready());
            while( true ) {
                String line = bufferedReader.readLine(); //TRAVA AQUI
                if( line == null ) break;
                System.out.println( line );
            }
        } catch( Exception e ) {
            e.printStackTrace();
        }
    }
    
    public String getProcessName() {
        return processName;
    }
    
    public void setProcessName(String processName) {
        this.processName = processName;
    }
}

Então resolvi fazer um teste q consiste em rodar o CMD.EXE do windows
e emitir um comando DIR no OutputStream e pegar o resultado no
InputStream utilizando a classe acima.

public class Tests {
    
    public void testProcess() {
        try {

            Process process = Runtime.getRuntime().exec( "C:\\WINNT\\system32\\CMD.EXE" );
            ProcessInstance processInstance = new ProcessInstance( "PROCESSO(1)", process );
            
            //envia mensagem
            processInstance.input( "dir C:\\" );
            
            //mostra resultado da mensagem
            processInstance.output();

        } catch( Exception e ) {
            e.printStackTrace();
        }
    }
    
    public static void main( String args[] ) {
        Tests test = new Tests();
        test.testProcess();
    }
}

Acontece q quando ele tenta mostrar o resultado do comando DIR ele trava no método readLine() de BufferedReader.

O resultado da saída do programa está logo abaixo:

Microsoft Windows 2000 [VersÆo 5.00.2195]
(C) Copyright 1985-2000 Microsoft Corp.

C:\projetos\test>dir
 O volume na unidade C  Disco local
 O n£mero de srie do volume  5006-3678

 Pasta de C:\projetos\test

05/07/2005  13:18       <DIR>          .
05/07/2005  13:18       <DIR>          ..
05/07/2005  13:18       <DIR>          build
05/07/2005  11:03                3.349 build.xml
05/07/2005  11:03                3.349 build.xml~
05/07/2005  16:58       <DIR>          CVS
05/07/2005  10:59                   85 manifest.mf
05/07/2005  11:03       <DIR>          nbproject
05/07/2005  14:03       <DIR>          src
05/07/2005  10:59                  639 teste.txt
               4 arquivo(s)          7.422 bytes
               6 pasta(s)  2.804.244.480 bytes dispon¡veis

O processo fica travado no readLine(). Não emite nem uma Exception. Parece q fica procurando por algo.

O q pode ser? Tem alguma coisa errada no q eu fiz?

Valeu!

6 Respostas

T

Aham, parece que você esqueceu de digitar o enter
(isto é, enviá-lo, não sei se com “\r” ou com “\r\n”, ou mesmo com “\n”)
quando deu o “dir” :wink:

Sami_Koivu

Olá,

Ah mas, thingol, ele usa o método println, e assim o linebreak fica enviado, sim.

public void input( String message ) {
         processInput.println( message );
         processInput.flush();
     }

Acho que o problema é… hum…

Luiz, você usa o método readLine() da classe BufferedReader.

O que esse método faz, é ele fica lendo caráteres até que encontre um linebreak(um fim de linha? argh, desculpa não sei dizer isso em português) ou até que encontre o fim do stream.

Neste caso porém… como o DOS é interativo… depois de ter imprimido a saída do comando “dir” ele imprime:
C:\jbproject\playground> (no meu caso)
sem linebreak… e sem fechar o stream… pois ele tá esperando um novo comando. Então o método readLine() fica esperando um linebreak que nunca vai chegar… Infelizmente não sei o que seria uma boa solução para corrigir isso.

Abraços,
Sami

T

Então tem de enviar um “exit” para sair do cmd.exe.
Legal, Sami, não tinha visto que ele usava um println.

Sami_Koivu

heh… a solução que tava faltando… enviar um “exit” parece uma boa idéia :smiley: Ótimo.

-Sami

Luiz_Henrique_Coura

ae Pessoal, desculpa a demora!

A idéia do exit funciona legal, porém tem um problema:

Toda hora eu terei de abrir um processo externo (por exemplo, um CMD.EXE), enviar um comando para esse processo e em
seguida fechar esse processo. Isso não seria um gasto excessivo de processamento para o sistema?

Eu estava pensando em deixar esse processo aberto e apenas enviar os comandos para ele executar. No caso do CMD.EXE
eu poderia encaminhar a saída do comando DIR para um arquivo com DIR >> TESTE.TXT e depois analisar esse arquivo,
mas o sistema externo q vou trabalhar ele não aceita isso.

Então fiz o seguinte:

Na minha classe ProcessInstance eu implementei Runnable e coloquei o processo de imprimir dados de InputStream em um
processo paralelo. Ficou assim: (Fiz umas pequenas alterações com relação a primeira implementação)

public class ProcessInstance implements Runnable {
    private Process process;
    private PrintStream processInput;
    private BufferedReader processOutput;
    
    private String processName;
    
    public ProcessInstance( String processName, Process process ) {
        setProcessName( processName );
        setProcess( process );
    }
    
    public Process getProcess() {
        return process;
    }
    
    public void setProcess(Process process) {
        this.process = process;
        setStreams();
    }
    
    protected void setStreams() {
        this.processInput = new PrintStream( getProcess().getOutputStream() );
        this.processOutput = new BufferedReader( new InputStreamReader( getProcess().getInputStream() ) );
    }
    
    public void input( String message ) {
        processInput.println( message );
        processInput.flush();
    }
    
    public String getProcessName() {
        return processName;
    }
    
    public void setProcessName(String processName) {
        this.processName = processName;
    }
    
    public void run() {
        try {
            while( true ) {
                String line = processOutput.readLine();
                if( line == null ) break;
                if( line.length() != 0 ) System.out.println( line );
            }
        } catch( Exception e ) {
            e.printStackTrace();
        }
    }
}

Testando a classe:

public class Tests {
    public void testProcess() {
        try {
            Process process = Runtime.getRuntime().exec( "C:\\WINNT\\system32\\CMD.EXE" );
            ProcessInstance processInstance = new ProcessInstance( "PROCESSO(1)", process );
            
            //Inicío a Thread q vai capturar a saída do processo externo
            new Thread( processInstance ).start();
            
            //envio várias mensagens e em seguida eu fecho com exit 
            //(exit no caso de trabalhar com CMD.EXE)
            processInstance.input( "dir c:\\" );
            processInstance.input( "dir" );
            processInstance.input( "exit" );
            
        } catch( Exception e ) {
            e.printStackTrace();
        }
    }
    
    public static void main( String args[] ) {
        new Tests().testProcess();
    }
}

O método readLine() fica esperando o buffer apresentar pra ele um '\n' ou '\r' para ele retornar uma linha, enquanto isso
ele fica parado, sem gastar processamento de máquina.

O q vcs acham?

Sami_Koivu

Certo… pode até gastar o processamento. Depende - claro - do intervalo com que você pretende executar aquilo.

Essa esquema de usar um Thread provavelmente fica mais eficiente, sim. E eu pelo menos não vejo problema nisso. Legal :slight_smile:

-Sami

Criado 5 de julho de 2005
Ultima resposta 7 de jul. de 2005
Respostas 6
Participantes 3