streams sirve salida que para metodos manejo flujos estandar entrada ejemplos definicion datos archivos java process io

sirve - salida estandar en java



¿Cómo manejo múltiples flujos en Java? (2)

Estoy intentando ejecutar un proceso y hacer cosas con sus flujos de entrada, salida y error. La forma obvia de hacer esto es usar algo como select() , pero lo único que puedo encontrar en Java que hace eso es Selector.select() , que toma un Channel . No parece posible obtener un Channel de un InputStream o OutputStream ( FileStream tiene un método getChannel() pero eso no ayuda aquí)

Entonces, en cambio, escribí un código para sondear todas las transmisiones:

while( !out_eof || !err_eof ) { while( out_str.available() ) { if( (bytes = out_str.read(buf)) != -1 ) { // Do something with output stream } else out_eof = true; } while( err_str.available() ) { if( (bytes = err_str.read(buf)) != -1 ) { // Do something with error stream } else err_eof = true; } sleep(100); }

que funciona, excepto que nunca termina. Cuando una de las secuencias llega al final del archivo, available() devuelve cero, por lo que no se llama a read() y nunca obtenemos el retorno -1 que indicaría EOF.

Una solución sería una forma no bloqueante para detectar EOF. No puedo ver uno en los documentos en ningún lado. Alternativamente, ¿hay una mejor manera de hacer lo que quiero hacer?

Veo esta pregunta aquí: enlace el texto y aunque no hace exactamente lo que quiero, probablemente pueda usar esa idea, de generar hilos separados para cada flujo, para el problema particular que tengo ahora. Pero seguramente esa no es la única forma de hacerlo? ¿Seguramente debe haber una forma de leer de múltiples flujos sin usar un hilo para cada uno?


De hecho, tendrá que seguir la ruta de engendrar un hilo por cada flujo que desee monitorear. Si su caso de uso permite combinar stdout y stderr del proceso en cuestión, solo necesita un hilo, de lo contrario, se necesitan dos.

Me llevó bastante tiempo acertar en uno de nuestros proyectos en el que tuve que iniciar un proceso externo, sacar su resultado y hacer algo con él, al mismo tiempo que buscaba errores y terminaba el proceso y también podía darlo por terminado. cuando el usuario de la aplicación java cancela la operación.

Creé una clase bastante simple para encapsular la parte de observación cuyo método run () se parece a esto:

public void run() { BufferedReader tStreamReader = null; try { while (externalCommand == null && !shouldHalt) { logger.warning("ExtProcMonitor(" + (watchStdErr ? "err" : "out") + ") Sleeping until external command is found"); Thread.sleep(500); } if (externalCommand == null) { return; } tStreamReader = new BufferedReader(new InputStreamReader(watchStdErr ? externalCommand.getErrorStream() : externalCommand.getInputStream())); String tLine; while ((tLine = tStreamReader.readLine()) != null) { logger.severe(tLine); if (filter != null) { if (filter.matches(tLine)) { informFilterListeners(tLine); return; } } } } catch (IOException e) { logger.logExceptionMessage(e, "IOException stderr"); } catch (InterruptedException e) { logger.logExceptionMessage(e, "InterruptedException waiting for external process"); } finally { if (tStreamReader != null) { try { tStreamReader.close(); } catch (IOException e) { // ignore } } } }

En el lado de la llamada, se ve así:

Thread tExtMonitorThread = new Thread(new Runnable() { public void run() { try { while (externalCommand == null) { getLogger().warning("Monitor: Sleeping until external command is found"); Thread.sleep(500); if (isStopRequested()) { getLogger() .warning("Terminating external process on user request"); if (externalCommand != null) { externalCommand.destroy(); } return; } } int tReturnCode = externalCommand.waitFor(); getLogger().warning("External command exited with code " + tReturnCode); } catch (InterruptedException e) { getLogger().logExceptionMessage(e, "Interrupted while waiting for external command to exit"); } } }, "ExtCommandWaiter"); ExternalProcessOutputHandlerThread tExtErrThread = new ExternalProcessOutputHandlerThread("ExtCommandStdErr", getLogger(), true); ExternalProcessOutputHandlerThread tExtOutThread = new ExternalProcessOutputHandlerThread("ExtCommandStdOut", getLogger(), true); tExtMonitorThread.start(); tExtOutThread.start(); tExtErrThread.start(); tExtErrThread.setFilter(new FilterFunctor() { public boolean matches(Object o) { String tLine = (String)o; return tLine.indexOf("Error") > -1; } }); FilterListener tListener = new FilterListener() { private boolean abortFlag = false; public boolean shouldAbort() { return abortFlag; } public void matched(String aLine) { abortFlag = abortFlag || (aLine.indexOf("Error") > -1); } }; tExtErrThread.addFilterListener(tListener); externalCommand = new ProcessBuilder(aCommand).start(); tExtErrThread.setProcess(externalCommand); try { tExtMonitorThread.join(); tExtErrThread.join(); tExtOutThread.join(); } catch (InterruptedException e) { // when this happens try to bring the external process down getLogger().severe("Aborted because auf InterruptedException."); getLogger().severe("Killing external command..."); externalCommand.destroy(); getLogger().severe("External command killed."); externalCommand = null; return -42; } int tRetVal = tListener.shouldAbort() ? -44 : externalCommand.exitValue(); externalCommand = null; try { getLogger().warning("command exit code: " + tRetVal); } catch (IllegalThreadStateException ex) { getLogger().warning("command exit code: unknown"); } return tRetVal;

Lamentablemente, no es necesario para un ejemplo autónomo ejecutable, pero tal vez esto ayude. Si tuviera que volver a hacerlo, me gustaría echar un vistazo al uso del método Thread.interrupt () en lugar de un marcador de parada hecho por mí mismo (¡mente para declararlo volátil!), Pero lo dejo para otro momento. :)


Como dijo, la solución que se describe en esta respuesta es la forma tradicional de leer tanto stdout como stderr de un proceso. Un hilo por secuencia es el camino a seguir, a pesar de que es un poco molesto.