blob: 9790388b370cb46baa00f94c85b67f06d0c94d7d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ptp.internal.rdt.core.remotemake;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import org.eclipse.ptp.remote.core.IRemoteProcess;
/**
* @author crecoskie
*
* Bundled state of a launched process including the threads linking the process
* in/output to console documents.
*
* This class is a modified version of ProcessClosure from CDT that can handle remote processes.
*
* @see org.eclipse.cdt.internal.core.ProcessClosure
*/
public class RemoteProcessClosure {
/**
* Thread which continuously reads from a input stream and pushes the read
* data to an output stream which is immediately flushed afterwards.
*/
protected static class ReaderThread extends Thread {
private InputStream fInputStream;
private OutputStream fOutputStream;
private boolean fFinished = false;
private String lineSeparator;
/*
* outputStream can be null
*/
public ReaderThread(ThreadGroup group, String name, InputStream in, OutputStream out) {
super(group, name);
fOutputStream = out;
fInputStream = in;
setDaemon(true);
// TODO FIXME: line separator should be taken from the remote system... but how?
lineSeparator = System.getProperty("line.separator"); //$NON-NLS-1$
}
@Override
public void run() {
try {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(fInputStream));
String line;
while ((line = reader.readLine()) != null) {
line += lineSeparator;
if(fOutputStream != null)
fOutputStream.write(line.getBytes());
}
} catch (IOException x) {
// ignore
} finally {
try {
// writer.flush();
if(fOutputStream != null)
fOutputStream.flush();
} catch (IOException e) {
// ignore
}
try {
fInputStream.close();
} catch (IOException e) {
// ignore
}
}
} finally {
complete();
}
}
public synchronized boolean finished() {
return fFinished;
}
public synchronized void waitFor() {
while (!fFinished) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
public synchronized void complete() {
fFinished = true;
notify();
}
public void close() {
try {
fOutputStream.close();
} catch (IOException e) {
// ignore
}
}
}
protected static int fCounter = 0;
protected IRemoteProcess fProcess;
protected OutputStream fOutput;
protected OutputStream fError;
protected ReaderThread fOutputReader;
protected ReaderThread fErrorReader;
/**
* Creates a process closure and connects the launched process with a
* console document.
*
* @param outputStream
* prcess stdout is written to this stream. Can be
* <code>null</code>, if not interested in reading the output
* @param errorStream
* prcess stderr is written to this stream. Can be
* <code>null</code>, if not interested in reading the output
*/
public RemoteProcessClosure(IRemoteProcess process, OutputStream outputStream, OutputStream errorStream) {
fProcess = process;
fOutput = outputStream;
fError = errorStream;
}
/**
* Live links the launched process with the configured in/out streams using
* reader threads.
*/
public void runNonBlocking() {
ThreadGroup group = new ThreadGroup("CBuilder" + fCounter++); //$NON-NLS-1$
InputStream stdin = fProcess.getInputStream();
InputStream stderr = fProcess.getErrorStream();
fOutputReader = new ReaderThread(group, "OutputReader", stdin, fOutput); //$NON-NLS-1$
fErrorReader = new ReaderThread(group, "ErrorReader", stderr, fError); //$NON-NLS-1$
fOutputReader.start();
fErrorReader.start();
}
public void runBlocking() {
runNonBlocking();
boolean finished = false;
while (!finished && !fProcess.isCompleted()) {
try {
fProcess.waitFor();
} catch (InterruptedException e) {
//System.err.println("Closure exception " +e);
}
try {
fProcess.exitValue();
finished = true;
} catch (IllegalThreadStateException e) {
//System.err.println("Closure exception " +e);
}
}
// @@@FIXME: Windows 2000 is screwed; double-check using output threads
if (!fOutputReader.finished()) {
fOutputReader.waitFor();
}
if (!fErrorReader.finished()) {
fErrorReader.waitFor();
}
fOutputReader.close();
fErrorReader.close();
// it seems that thread termination and stream closing is working
// without
// any help
fProcess = null;
fOutputReader = null;
fErrorReader = null;
}
public boolean isAlive() {
if (fProcess != null) {
if (!fProcess.isCompleted() && (fOutputReader.isAlive() || fErrorReader.isAlive())) {
return true;
}
fProcess = null;
fOutputReader.close();
fErrorReader.close();
fOutputReader = null;
fErrorReader = null;
}
return false;
}
/**
* The same functionality as "isAlive()"
* but does not affect out streams,
* because they can be shared among processes
*/
public boolean isRunning() {
if (fProcess != null) {
if (fOutputReader.isAlive() || fErrorReader.isAlive()) {
return true;
}
fProcess = null;
}
return false;
}
/**
* Forces the termination the launched process
*/
public void terminate() {
if (fProcess != null) {
fProcess.destroy();
fProcess = null;
}
if (!fOutputReader.finished()) {
fOutputReader.waitFor();
}
if (!fErrorReader.finished()) {
fErrorReader.waitFor();
}
fOutputReader.close();
fErrorReader.close();
fOutputReader = null;
fErrorReader = null;
}
}