Erro ao tratar InputStream de Process

Falae galera!

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

[code]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;
}

}[/code]

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.

[code]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();
}

}[/code]

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:

[code]Microsoft Windows 2000 [VersÆo 5.00.2195]
© Copyright 1985-2000 Microsoft Corp.

C:\projetos\test>dir
O volume na unidade C ‚ Disco local
O n£mero de s‚rie 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
[/code]

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!

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:

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

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

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

-Sami

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)

[code]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();
    }
}

}[/code]

Testando a classe:

[code]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();
}

}[/code]

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?

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