Como rodar um programa externo?

2 respostas
arthurthiago

Fala galera!

Estou a um tempo utilizando esta classe abaixo para executar um programa .exe externo (feito em pascal) pelo command do Windows, jogar uma entrada neste programa e pegar a saída dele:

package corretor;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import javax.sound.midi.SysexMessage;
import logica.Constantes;

/**
 * Classe que executa processos externos.
 */
public class Executador {
    
    private int exitVal = 0;
    private BufferedReader br = null;
    private String[] args = null;
    private File diretorio = null ;
    private String entrada = null;
    private String saida = null;
    private long tempoExecucao = 0;
    private StreamGobbler outputGobbler = null;
    private static Process proc = null;
    boolean travou = false;
 
    /**
     * Cria uma nova instância da classe Executador.
     * @param diretorio O diretório padrão que terá o processo.
     * @param args Um vetor de argumentos que serão executados no processo.
     * @param entrada A entrada que o processo terá.
     */
    public Executador(File diretorio, String[] args, String entrada) {
        this.diretorio = diretorio;
        this.args = args;
        this.entrada = entrada;
    }

    /**
     * Executa um determinado processo com os parâmetros passados no construtor.
     */
    public void executar() {
        try {
            Runtime rt = Runtime.getRuntime();
            System.out.println("Setando diretório: " + diretorio.getAbsolutePath());
            String texto = "";
            for (int i = 0; i <= args.length - 1; i++) {
                texto += args[i] + " ";
            }
            System.out.println("Executando:" + texto);
            String[] env = new String[] {"PATH=" + System.getenv("PATH")};
            proc = rt.exec(args, env, diretorio);
            
            // alguma mensagem de erro?
            StreamGobbler errorGobbler = new 
                StreamGobbler(proc.getErrorStream(), "ERRO");

            // alguma saída?
            outputGobbler = new StreamGobbler(proc.getInputStream(), "SAÍDA");

            long tempoInicial = System.currentTimeMillis();
            errorGobbler.start();
            outputGobbler.start();
            
            if (entrada != null) {
                OutputStream out = proc.getOutputStream();
                out.write((entrada).getBytes());
                out.flush();
                out.close();
                
                testarTravamento();
                
                exitVal = proc.waitFor();
                proc.destroy();
                outputGobbler.parar();
                saida = outputGobbler.getSaida();
            } else {
                testarTravamento();
                exitVal = proc.waitFor();
            }
            tempoExecucao = System.currentTimeMillis() - tempoInicial;
             
            System.out.println("Valor de Saída: " + exitVal);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
    
    /**
     * Retorna o tempo de execução do processo.
     */
    public long getTempoExecucao() {
        return tempoExecucao;
    }
    
    /**
     * Retorna a saída do processo.
     */
    public String getSaida() {
        return saida;
    }
    
    /**
     * Retorna o valor de saída do processo.
     */
    public int getValorSaida() {
        return exitVal;
    }
    
    private void testarTravamento() {
        travou = false;
        new Thread(new Runnable() {
            public void run() {
                try {
                    Thread.currentThread().sleep(Constantes.getTempoMaximoExecucao());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (outputGobbler.isAlive()) {
                    travou = true;
                    proc.destroy();
                    String processo = args[2].substring(0, args[2].length() - 4);
                    String[] argsD = new String[] { "cmd", "/C", "tskill", processo};
                    Executador exec = new Executador(diretorio, argsD, null);
                    exec.executar();
                }
            } 
        }).start();
    }
    
    /**
     * Classe interna utilizada para capturar as saídas e mensagens de erro
     * de um determinado processo.
     */
    class StreamGobbler extends Thread {
        InputStream is = null;
        String type = null;
        String saida = null;
        boolean flagParar = false;
        
        public void parar() {  
            flagParar = true;  
        } 

        StreamGobbler(InputStream is, String type) {
            this.is = is;
            this.type = type;
            this.saida = new String();
        }

        public void run() {
            try {
                InputStreamReader isr = new InputStreamReader(is);
                br = new BufferedReader(isr);
                String line = null;
                while ( (line = br.readLine()) != null) {
                    System.out.println(type + ">" + line);
                    if (saida != null) {
                        saida += line + "\n";
                    }
                    if (flagParar) {
                        System.out.flush();
                        return;
                    }
                }
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }

        /**
         * Retorna a saída do processo.
         */
        public String getSaida() {
            return saida;
        }
    }
}

Algumas explicações do código:

Eu executo processos externos por meio desta classe. Quando eu preciso jogar uma entrada no programa é só passar no Construtor uma entrada não nula. Depois de executado eu posso pegar a saída do programa pelo método getSaida() (linha 175).

A Classe interna StreamGobbler serve para ouvir as mensagens que são escritas na saída, que não sejam parte da execução do processo. Normalmente mensagens de erro e de instruções para uso do FPC no meu caso.

O método testarTravamento() (linha 111) serve para estipular um tempo máximo para que o pocesso rode. Caso ele estrapole este limite o processo é encerrado manualmente por meio da execução de outro processo que chama a função "TSKILL" do command do windows.

Um exemplo de utilização desta classe:

String[] args = new String[] { "cmd", "/C", "programa.exe" };
File diretorio = new File("c:\Programas em Teste\");
String entrada = "1 5 10\nPedro\n9";

Executador ex = new Executador(diretorio, args, entrada);
ex.executar();
long tempoExecucao = ex.getTempoExecucao();
String saida = ex.getSaida();

O Problema que eu tenho tido está dentro do trecho de código quando a entrada não é nula! (linha 66), mais especificamente aqui:

OutputStream out = proc.getOutputStream();  
out.write((entrada).getBytes());  
out.flush();  
out.close();

Aqui eu jogo no command do windows a entrada que passo pelo construtor da classe. Porém as vezes funciona corretamente e às vezes não!
O que ocorre é que existem vezes que eu jogo a entrada e a saída vem cortada, incompleta! Percebi isso depois de fazer alguns testes diretamente no command do windows, sem o intermédio do programa.

Alguém pode me ajudar? Alguma idéia?

2 Respostas

dmandrak

Pow cara, quando eu li o nome do tópico achei que vc precisasse de ajuda pra rodar.

Eu ia sugerir o JavaRobot mas como vc jah fez tudo direito…

O robot executa comandos, mas não dá retorno.

Não posso te ajudar :wink: nesse caso.

Também não entendi porque com vc não funciona com estabilidade.

T

O problema está no loop while da classe StreamGobbler.

Tente algo como isso 8)

while (!flagParar) {
		    if ( (line = br.readLine()) != null){
	                    System.out.println(type + ">" + line);
        	            if (saida != null) {
                	        saida += line + "\n";
	                    }
		    }

                }
System.out.flush();
Criado 19 de novembro de 2008
Ultima resposta 28 de jan. de 2009
Respostas 2
Participantes 3