O processo filho do Windows Java não entra ou sai quando definido como IO padrão do pai (Prompt de comando)
No Windows, não consigo manipular de forma confiável o E / S do meu processo filho quando meu programa foi iniciado a partir da linha de comando. É frustrante, pois é padrão para os servidores usarem um console para E / S. GUIs são legais, mas eu realmente prefiro manter a linha de comando e manter as coisas simples. Eu notei que a E / S do processo filho está bem quando estou executando meu servidor a partir do Eclipse IDE, mas é uma história completamente diferente sendo executada a partir da linha de comando. Não consigo ler ou escrever no processo filho, mas o processo ainda estaria em execução. Eu escrevi algum código de teste abaixo que demonstra esse problema, e espero que o problema possa ser reproduzido em outra máquina e esperamos obter uma solução dele. Quando executado a partir do Eclipse, a E / S herdada funciona conforme o esperado. No entanto, quando executado a partir do prompt de comando do Windows, nada pode ser lido ou gravado no processo filho. Em ambos os casos, o redirecionamento da saída do processo filho para um arquivo sempre é bem-sucedido, mas a entrada ainda não pode ser passada para o filho. Se já houver uma solução para este problema, por favor, vincule a página.
Implementação do JRE / JDK:
>java -version
java version "1.7.0_01"
Java(TM) SE Runtime Environment (build 1.7.0_01-b08)
Java HotSpot(TM) 64-Bit Server VM (build 21.1-b02, mixed mode)
Considere o seguinte código:
package com.comp8nerd4u2.io.test;
/*
* These tests attempt to confirm what I'm experiencing under my build environment
*/
import java.io.File;
import java.io.IOException;
public final class PIOTest {
/** The command to run as a child process. The command itself isn't the test, but what you use to run this Java program is the test. */
private static final String[] COMMAND = {"cmd.exe", "/c", "echo This is a test. Feel free to change this."}; // Change this to just {"cmd.exe"} or some other program that accepts input and you'll see how frustrating this is
/** Controls how the test process is built */
private static final ProcessBuilder PB = new ProcessBuilder(COMMAND);
/** How long to allow the process to run before forcibly terminating it. */
private static final long PROCESS_TIMEOUT = 10000L;
private static final Runnable R = new TimedInterruptWorker(PROCESS_TIMEOUT);
private static int n = 0;
static {
PB.redirectErrorStream(true);
}
private PIOTest() {}
public static void main(String[] args) {
// ----- Begin Tests -----
/*
* Test #1: Let's test putting our command's output onto our standard I/O streams
* Goal condition: Child process outputs expected output, and exits before the timeout. If child process expects input, it should accept entered input.
* Known success factors: Parent process' standard I/O is piped to Eclipse. Tests would probably succeed with Netbeans as well
* Known fail factors: Parent process' standard I/O is piped to Windows Command Prompt
* Result under fail condition: Child process hangs if it fills up its output buffer or requests input, but exits on its own otherwise, unless it took longer than the timeout.
*/
PB.inheritIO();
doTest();
// Test #2: Let's test putting our command's output into a file
PB.redirectOutput(new File("piotest.txt"));
doTest();
}
/**
* Performs the I/O test.
*/
private static void doTest() {
n++;
Process p = null;
try {
p = PB.start();
} catch (IOException e) {
e.printStackTrace();
return;
}
try {
Thread t = new Thread(R);
t.setDaemon(true);
t.start();
System.out.format("[Test #%d] Child exited with status code %d\n", n, p.waitFor());
t.interrupt();
} catch (InterruptedException e) {
p.destroy();
System.out.format("[Test #%d] Child took longer than the timeout.\n", n);
}
}
/**
* Useful for sending interrupts after a certain amount of time has passed.
*
* @author comp8nerd4u2
*/
private static final class TimedInterruptWorker implements Runnable {
private long timeout = 0;
private Thread target = null;
public TimedInterruptWorker(long timeout) {
this(timeout, Thread.currentThread());
}
public TimedInterruptWorker(long timeout, Thread target) {
this.timeout = timeout;
this.target = target;
}
@Override
public void run() {
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
return;
}
target.interrupt();
}
}
}
ATUALIZAÇÃO: Eu modifiquei o teste para aceitar qualquer comando em tempo de execução, e enviei para o meu servidor vps linux. Eu executei-o a partir de uma sessão ssh e todos os processos filhos de E / S podem ser lidos e gravados com facilidade. Houve uma coisa que eu notei. Quando eu abri um shell bash interativo como um processo filho, e então redireciono sua saída para um arquivo, o CentOS parou meu programa, eu acho. Isso ou meu programa caiu.
[admin@comp8nerd4u2 piotest]$ java -jar piotest.jar
Enter command to run : bash
[admin@comp8nerd4u2 piotest]$ [Test #1] Child took longer than the timeout.
[1]+ Stopped java -jar piotest.jar
[admin@comp8nerd4u2 piotest]$
A primeira linha é minha digitação no comando. A segunda linha é o shell bash que foi gerado, mas eu nunca digitei nada nele, então meu programa o mata após o tempo limite. Ele fica pronto para o segundo teste, cria o arquivo "piotest.txt" e, em seguida, trava ou é interrompido pelo sistema operacional. O teste real em si não foi alterado, exceto que o teste agora permite inserir o comando a ser executado no tempo de execução. Isso funciona bem no linux, mas não no Windows. Espero que alguém que conheça a API do Win32 possa de alguma forma explicar por que esse teste falha no Windows.