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?
nesse caso.