// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved. // Released under the terms of the CPL Common Public License version 1.0. package fitnesse.components; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import util.StreamReader; import fit.Counts; import fit.FitProtocol; import fitnesse.responders.run.TestSummary; import fitnesse.responders.run.TestSystemListener; public class FitClient { protected TestSystemListener listener; protected Socket fitSocket; private OutputStream fitInput; private StreamReader fitOutput; private volatile int sent = 0; private volatile int received = 0; private volatile boolean isDoneSending = false; protected volatile boolean killed = false; protected Thread fitListeningThread; public FitClient(TestSystemListener listener) { this.listener = listener; } public void acceptSocket(Socket socket) throws IOException, InterruptedException { checkForPulse(); fitSocket = socket; fitInput = fitSocket.getOutputStream(); FitProtocol.writeData("", fitInput); fitOutput = new StreamReader(fitSocket.getInputStream()); fitListeningThread = new Thread(new FitListeningRunnable(), "FitClient fitOutput"); fitListeningThread.start(); } public void send(String data) throws IOException, InterruptedException { checkForPulse(); FitProtocol.writeData(data, fitInput); sent++; } public void done() throws IOException, InterruptedException { checkForPulse(); FitProtocol.writeSize(0, fitInput); isDoneSending = true; } public void join() { if (fitListeningThread != null) try { fitListeningThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } public void kill() { killed = true; if (fitListeningThread != null) fitListeningThread.interrupt(); } public void exceptionOccurred(Exception e) { listener.exceptionOccurred(e); } protected void checkForPulse() throws InterruptedException { if (killed) throw new InterruptedException("FitClient was killed"); } private void listenToFit() { try { attemptToListenToFit(); } catch (Exception e) { exceptionOccurred(e); } } private void attemptToListenToFit() throws Exception { while (!finishedReading()) { int size; size = FitProtocol.readSize(fitOutput); if (size != 0) { String readValue = fitOutput.read(size); if (fitOutput.byteCount() < size) throw new Exception("I was expecting " + size + " bytes but I only got " + fitOutput.byteCount()); listener.acceptOutputFirst(readValue); } else { Counts counts = FitProtocol.readCounts(fitOutput); TestSummary summary = new TestSummary(); summary.right = counts.right; summary.wrong = counts.wrong; summary.ignores = counts.ignores; summary.exceptions = counts.exceptions; listener.testComplete(summary); received++; } } } private boolean finishedReading() { while (stateIndeterminate()) shortSleep(); return isDoneSending && received == sent; } /** * @return true if the current state of the transission is indeterminate. * <p/> * When the number of pages sent and recieved is the same, we may be done with the whole job, * or we may just be waiting for FitNesse to send the next page. There's no way to know until * FitNesse either calls send, or done. */ private boolean stateIndeterminate() { return (received == sent) && !isDoneSending; } private void shortSleep() { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } private class FitListeningRunnable implements Runnable { public void run() { listenToFit(); } } }