瀏覽代碼

add 3-party cli

Boyoung- 8 年之前
父節點
當前提交
3dc05c1ca5

+ 670 - 0
src/communication/Communication.java

@@ -0,0 +1,670 @@
+package communication;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import util.Util;
+
+/**
+ * Basic Usage
+ * 
+ * 1. Call {@link #start(int)} or {@link #connect(InetSocketAddress)} to
+ * initiate a connection 2. Wait for {@link #getState()} to return
+ * {@link #STATE_CONNECTED} 3. {@link #write(byte[])} and {@link #read()}
+ * messages. 4. Close the connection with {@link #stop()}. NOTE: This may
+ * invalidate unread data
+ * 
+ * Alternatively, you can always call start. And the first side of the
+ * connection to call connect will win.
+ */
+public class Communication {
+
+	public static boolean D = false;
+
+	// Constants that indicate the current connection state
+	public static final int STATE_NONE = 0; // we're doing nothing
+	public static final int STATE_LISTEN = 1; // now listening for incoming
+												// connections
+	public static final int STATE_CONNECTING = 2; // now initiating an outgoing
+													// connection
+	public static final int STATE_CONNECTED = 3; // now connected to a remote
+													// device
+	public static final int STATE_STOPPED = 4; // we're shutting things down
+	public static final int STATE_RETRY = 5; // we are going to retry, but first
+												// we listen
+
+	private AcceptThread mSecureAcceptThread;
+	private ConnectThread mConnectThread;
+	private ConnectedThread mConnectedThread;
+
+	// Maximum reconnect attempts
+	private static final int MAX_RETRY = 2;
+
+	/***********************
+	 * Private Members
+	 **********************/
+	// Current number of reconnect attempts
+	private int mNumTries;
+	private int mPort = 0;
+
+	private boolean acceptMode = false;
+
+	protected int mState;
+	protected InetSocketAddress mAddress;
+
+	public Communication() {
+		mState = STATE_NONE;
+	}
+	
+	public void setTcpNoDelay(boolean on) {
+		if (mConnectedThread != null)
+			mConnectedThread.setTcpNoDelay(on);
+	}
+
+	/**
+	 * Set the current state of the connection
+	 * 
+	 * @param state
+	 *            An integer defining the current connection state
+	 */
+	protected synchronized void setState(int state) {
+		if (D)
+			Util.debug("setState() " + mState + " -> " + state);
+		mState = state;
+	}
+
+	/**
+	 * Return the current connection state.
+	 */
+	public synchronized int getState() {
+		return mState;
+	}
+
+	/**
+	 * Start the communication service. Specifically start AcceptThread to begin
+	 * a session in listening (server) mode.
+	 */
+	public synchronized void start(int port) {
+		if (D)
+			Util.debug("start");
+
+		acceptMode = true;
+
+		startAcceptThread(port);
+
+		mPort = port;
+		mNumTries = 0;
+
+		setState(STATE_LISTEN);
+	}
+
+	private synchronized void startAcceptThread(int port) {
+		// Cancel any thread attempting to make a connection
+		if (mConnectThread != null) {
+			mConnectThread.cancel();
+			mConnectThread = null;
+		}
+
+		// Cancel any thread currently running a connection
+		if (mConnectedThread != null) {
+			mConnectedThread.cancel();
+			mConnectedThread = null;
+		}
+
+		// Start the thread to listen on a ServerSocket
+		if (mSecureAcceptThread == null) {
+			mSecureAcceptThread = new AcceptThread(port);
+			mSecureAcceptThread.start();
+		}
+	}
+
+	protected synchronized void retry() {
+		if (D)
+			Util.debug("retry");
+
+		if (D)
+			Util.debug("Retrying in state: " + getState());
+
+		if (mState == STATE_CONNECTED)
+			return;
+
+		// TODO: Does this logic belong here
+		if (mNumTries >= MAX_RETRY) {
+			signalFailed();
+
+			if (acceptMode)
+				start(mPort);
+			return;
+		}
+
+		startAcceptThread(mPort);
+
+		setState(STATE_RETRY);
+
+		int sleep = (int) (Math.random() * 1000 + 100);
+		if (D)
+			Util.debug("Sleeping: " + sleep);
+		try {
+			Thread.sleep(sleep);
+		} catch (InterruptedException e) {
+			Util.debug("Sleep interupted");
+		} // TODO: This may block the main thread?
+
+		if (D)
+			Util.debug("Waking up: " + getState());
+
+		// TODO: make this less strict
+		if (mState != STATE_CONNECTING && mState != STATE_CONNECTED
+				&& mConnectedThread == null && mConnectThread == null)
+			connect(mAddress);
+	}
+
+	/**
+	 * Start the ConnectThread to initiate a connection to a remote device.
+	 * 
+	 * @param address
+	 *            The address of the server
+	 * @param secure
+	 *            Socket Security type - Secure (true) , Insecure (false)
+	 */
+	public synchronized void connect(InetSocketAddress address) {
+		if (D)
+			Util.disp("connect to: " + address);
+
+		// Don't throw out connections if we are already connected
+		if (mState == STATE_CONNECTING || mConnectedThread != null) {
+			return;
+		}
+
+		mNumTries++;
+		mAddress = address;
+
+		// Cancel any thread attempting to make a connection
+		if (mState == STATE_CONNECTING) {
+			if (mConnectThread != null) {
+				mConnectThread.cancel();
+				mConnectThread = null;
+			}
+		}
+
+		// Cancel any thread currently running a connection
+		if (mConnectedThread != null) {
+			mConnectedThread.cancel();
+			mConnectedThread = null;
+		}
+
+		// Start the thread to connect with the given device
+		mConnectThread = new ConnectThread(address);
+		mConnectThread.start();
+		setState(STATE_CONNECTING);
+	}
+
+	/**
+	 * Start the ConnectedThread to begin managing a connection
+	 * 
+	 * @param socket
+	 *            The Socket on which the connection was made
+	 */
+	public synchronized void connected(Socket socket) {
+		if (D)
+			Util.debug("connected");
+
+		// Cancel the thread that completed the connection
+		if (mConnectThread != null) {
+			mConnectThread.cancel();
+			mConnectThread = null;
+		}
+
+		// Cancel any thread currently running a connection
+		if (mConnectedThread != null) {
+			mConnectedThread.cancel();
+			mConnectedThread = null;
+		}
+
+		// Cancel the accept thread because we only want to connect to one
+		// device
+		if (mSecureAcceptThread != null) {
+			mSecureAcceptThread.cancel();
+			mSecureAcceptThread = null;
+		}
+
+		// Start the thread to manage the connection and perform transmissions
+		mConnectedThread = new ConnectedThread(socket);
+		mConnectedThread.start();
+
+		setState(STATE_CONNECTED);
+	}
+
+	protected void connectionFailed() {
+		Util.error("Connection to the device failed");
+
+		// Start the service over to restart listening mode
+		if (getState() != STATE_STOPPED)
+			retry();
+	}
+
+	/**
+	 * Indicate that the connection was lost and notify the UI Activity.
+	 */
+	protected void connectionLost() {
+		if (D)
+			Util.error("Connection to the device lost");
+
+		// Start the service over to restart listening mode
+		if (getState() != STATE_STOPPED && acceptMode) {
+			start(mPort);
+		}
+	}
+
+	protected void signalFailed() {
+		// TODO:
+	}
+
+	/**
+	 * Stop all threads
+	 */
+	public synchronized void stop() {
+		if (D)
+			Util.debug("stop");
+		setState(STATE_STOPPED);
+
+		if (mConnectedThread != null) {
+			mConnectedThread.cancel();
+			mConnectedThread = null;
+		}
+
+		if (mConnectThread != null) {
+			mConnectThread.cancel();
+			mConnectThread = null;
+		}
+
+		if (mSecureAcceptThread != null) {
+			mSecureAcceptThread.cancel();
+			mSecureAcceptThread = null;
+		}
+	}
+
+	/**
+	 * Write to the ConnectedThread in an unsynchronized manner
+	 * 
+	 * This does not add message boundries!!
+	 * 
+	 * @param out
+	 *            The bytes to write
+	 * @see ConnectedThread#write(byte[])
+	 */
+	public void write(byte[] out) {
+		// Create temporary object
+		ConnectedThread r;
+		// Synchronize a copy of the ConnectedThread
+		synchronized (this) {
+			if (mState != STATE_CONNECTED)
+				return;
+			r = mConnectedThread;
+		}
+		// Perform the write unsynchronized
+		r.write(out);
+	}
+
+	/**
+	 * Write a length encoded byte array.
+	 * 
+	 * @param out
+	 */
+	public void writeLengthEncoded(byte[] out) {
+		write("" + out.length);
+		write(out);
+	}
+
+	public static final Charset defaultCharset = Charset.forName("ASCII");
+
+	// TODO: Rather than having millions of write/read methods can we take
+	// advantage of DataStreams?
+	public void write(String buffer) {
+		write(buffer, defaultCharset);
+	}
+
+	/*
+	 * This was added to allow backwords compaitibility with older versions
+	 * which used the default charset (usually utf-8) instead of asc-ii. This is
+	 * almost never what we want to do
+	 */
+	public void write(String buffer, Charset charset) {
+		write(buffer.getBytes(charset));
+		if (D)
+			Util.debug("Write: " + buffer);
+	}
+
+	/**
+	 * Read a string from Connected Thread
+	 * 
+	 * @see #read()
+	 */
+	public String readString() {
+		return new String(read());
+	}
+
+	/**
+	 * Read from the ConnectedThread in an unsynchronized manner Note, this is a
+	 * blocking call
+	 * 
+	 * @return the bytes read
+	 * @see ConnectedThread#read()
+	 */
+	public byte[] read() {
+		// Create temporary object
+		ConnectedThread r;
+		// Synchronize a copy of the ConnectedThread
+		synchronized (this) {
+			if (mState != STATE_CONNECTED)
+				return null;
+			r = mConnectedThread;
+		}
+
+		// Perform the read unsynchronized and parse
+		byte[] readMessage = r.read();
+
+		if (D)
+			Util.debug("Read: " + new String(readMessage));
+		return readMessage;
+	}
+
+	/**
+	 * Read a specific number of bytes from the ConnectedThread in an
+	 * unsynchronized manner Note, this is a blocking call
+	 * 
+	 * @return the bytes read
+	 * @see ConnectedThread#read()
+	 */
+	public byte[] readLengthEncoded() {
+		int len = Integer.parseInt(readString());
+		ArrayList<byte[]> bytes = new ArrayList<byte[]>();
+		byte[] data = read();
+		if (data.length != len) {
+			bytes.add(data);
+			data = read();
+		}
+
+		byte[] total = new byte[len];
+		int offset = 0;
+		for (byte[] b : bytes) {
+			for (int i = 0; i < b.length; i++) {
+				total[offset++] = b[i];
+			}
+		}
+
+		return total;
+	}
+
+	/**
+	 * This thread runs while listening for incoming connections. It behaves
+	 * like a server-side client. It runs until a connection is accepted (or
+	 * until cancelled).
+	 */
+	private class AcceptThread extends Thread {
+		// The local server socket
+		private final ServerSocket mmServerSocket;
+
+		public AcceptThread(int port) {
+			ServerSocket tmp = null;
+			try {
+				tmp = new ServerSocket(port);
+			} catch (IOException e) {
+				Util.error("ServerSocket unable to start", e);
+			}
+
+			mmServerSocket = tmp;
+		}
+
+		public void run() {
+			if (D)
+				Util.disp("BEGIN mAcceptThread ");
+			setName("AcceptThread");
+
+			Socket socket = null;
+
+			// Listen to the server socket if we're not connected
+			while (mState != STATE_CONNECTED) {
+				try {
+					// This is a blocking call and will only return on a
+					// successful connection or an exception
+					socket = mmServerSocket.accept();
+					//socket.setTcpNoDelay(true);
+				} catch (IOException e) {
+					Util.error("accept() failed", e);
+					break;
+				}
+
+				// If a connection was accepted
+				if (socket != null) {
+					synchronized (Communication.this) {
+						switch (mState) {
+						case STATE_LISTEN:
+						case STATE_CONNECTING:
+							// Situation normal. Start the connected thread.
+							connected(socket);
+							break;
+						case STATE_NONE:
+						case STATE_CONNECTED:
+							// Either not ready or already connected.
+							// Terminate new socket.
+							try {
+								socket.close();
+							} catch (IOException e) {
+								Util.error("Could not close unwanted socket", e);
+							}
+
+							// TODO: Should we really be returning here?
+							return;
+						}
+					}
+				}
+			}
+			if (D)
+				Util.disp("END mAcceptThread");
+
+		}
+
+		public void cancel() {
+			if (D)
+				Util.debug("AcceptThread canceled " + this);
+			try {
+				mmServerSocket.close();
+			} catch (IOException e) {
+				Util.error("close() of server failed", e);
+			}
+		}
+	}
+
+	/**
+	 * This thread runs while attempting to make an outgoing connection with a
+	 * device. It runs straight through; the connection either succeeds or
+	 * fails.
+	 */
+	private class ConnectThread extends Thread {
+		private final Socket mmSocket;
+		private final InetSocketAddress mmAddress;
+
+		public ConnectThread(InetSocketAddress address) {
+			mmAddress = address;
+
+			mmSocket = new Socket();
+			/*
+			try {
+				mmSocket.setTcpNoDelay(true);
+			} catch (SocketException e) {
+				e.printStackTrace();
+			}
+			*/
+		}
+
+		public void run() {
+			Util.debug("BEGIN mConnectThread");
+			setName("ConnectThread");
+
+			try {
+				// This is a blocking call and will only return on a
+				// successful connection or an exception
+				mmSocket.connect(mmAddress);
+			} catch (IOException e) {
+				// Close the socket
+				try {
+					mmSocket.close();
+				} catch (IOException e2) {
+					Util.error(
+							"unable to close() socket during connection failure",
+							e2);
+				}
+				connectionFailed();
+				return;
+			}
+
+			// Reset the ConnectThread because we're done
+			synchronized (Communication.this) {
+				mConnectThread = null;
+			}
+
+			// Start the connected thread
+			connected(mmSocket);
+		}
+
+		public void cancel() {
+			try {
+				mmSocket.close();
+			} catch (IOException e) {
+				Util.error("close() of connect socket failed", e);
+			}
+		}
+	}
+
+	/**
+	 * This thread runs during a connection with a remote device. It handles all
+	 * incoming and outgoing transmissions.
+	 */
+	private class ConnectedThread extends Thread {
+		private final Socket mmSocket;
+		private final DataInputStream mmInStream;
+		private final DataOutputStream mmOutStream;
+
+		private BlockingQueue<byte[]> mMessageBuffer;
+
+		public ConnectedThread(Socket socket) {
+			Util.debug("create ConnectedThread");
+			mmSocket = socket;
+			DataInputStream tmpIn = null;
+			DataOutputStream tmpOut = null;
+			mMessageBuffer = new LinkedBlockingQueue<byte[]>(); // TODO: add a
+			// capacity here
+			// to prevent
+			// doS
+
+			// Get the Socket input and output streams
+			try {
+				tmpIn = new DataInputStream(socket.getInputStream());
+				tmpOut = new DataOutputStream(socket.getOutputStream());
+
+			} catch (StreamCorruptedException e) {
+				Util.error("object streams corrupt", e);
+			} catch (IOException e) {
+				Util.error("temp sockets not created", e);
+			}
+
+			mmInStream = tmpIn;
+			mmOutStream = tmpOut;
+		}
+		
+		public void setTcpNoDelay(boolean on) {
+			if (mmSocket != null)
+				try {
+					mmSocket.setTcpNoDelay(on);
+				} catch (SocketException e) {
+					e.printStackTrace();
+				}
+		}
+
+		/**
+		 * Read from the ConnectedThread in an unsynchronized manner
+		 * 
+		 * This is a blocking call and will only return data if the readLoop
+		 * flag is false
+		 * 
+		 * @return the bytes read
+		 * @see ConnectedThread#read()
+		 */
+		public byte[] read() {
+			try {
+				return mMessageBuffer.take();
+			} catch (InterruptedException e) {
+				Util.error("Message Read Interupted");
+				return null;
+			}
+		}
+
+		/**
+		 * Write to the connected OutStream.
+		 * 
+		 * @param buffer
+		 *            The bytes to write
+		 */
+		public void write(byte[] buffer) {
+			try {
+				mmOutStream.writeInt(buffer.length);
+				mmOutStream.write(buffer);
+				mmOutStream.flush();
+			} catch (IOException e) {
+				Util.error("Exception during write", e);
+			}
+		}
+
+		public void run() {
+			Util.disp("BEGIN mConnectedThread");
+
+			int bytes;
+
+			// Keep listening to the InputStream while connected
+			while (true) {
+				try {
+					// Read from the InputStream
+					bytes = mmInStream.readInt();
+					byte[] buffer = new byte[bytes]; // TODO: This is a little
+					// dangerous
+
+					mmInStream.readFully(buffer, 0, bytes);
+
+					try {
+						mMessageBuffer.put(buffer);
+					} catch (InterruptedException e) {
+						Util.error("Message add interupted.");
+						// TODO: possibly move this catch elsewhere
+					}
+
+				} catch (IOException e) {
+					if (D)
+						Util.debug("Device disconnected");
+					connectionLost();
+					break;
+				}
+			}
+		}
+
+		public void cancel() {
+			try {
+				mmInStream.close();
+				mmOutStream.close();
+				mmSocket.close();
+			} catch (IOException e) {
+				Util.error("close() of connect socket failed", e);
+			}
+		}
+	}
+}

+ 16 - 0
src/exceptions/NoSuchPartyException.java

@@ -0,0 +1,16 @@
+package exceptions;
+
+public class NoSuchPartyException extends RuntimeException {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1L;
+
+	public NoSuchPartyException() {
+		super();
+	}
+
+	public NoSuchPartyException(String message) {
+		super(message);
+	}
+}

+ 12 - 6
src/oram/Forest.java

@@ -21,7 +21,7 @@ public class Forest implements Serializable {
 	private static final long serialVersionUID = 1L;
 
 	private static String folderName = "data/";
-	private String defaultFileName;
+	private String forestFileName;
 
 	private int tau;
 	private int addrBits;
@@ -50,11 +50,16 @@ public class Forest implements Serializable {
 		init(md);
 		initTrees(md, rand);
 	}
+	
+	public Forest(Metadata md, Random rand) {
+		init(md);
+		initTrees(md, rand);
+	}
 
 	// only used in xor operation
 	// does not shallow/deep copy trees
 	private Forest(Forest f) {
-		defaultFileName = f.getDefaultFileName();
+		forestFileName = f.getForestFileName();
 		tau = f.getTau();
 		addrBits = f.getAddrBits();
 		numInsertRecords = f.getNumInsertRecords();
@@ -63,15 +68,15 @@ public class Forest implements Serializable {
 	}
 
 	private void init(Metadata md) {
-		defaultFileName = md.getDefaultForestFileName();
+		forestFileName = md.getDefaultForestFileName();
 		tau = md.getTau();
 		addrBits = md.getAddrBits();
 		numInsertRecords = md.getNumInsertRecords();
 		numBytes = md.getForestBytes();
 	}
 
-	public String getDefaultFileName() {
-		return defaultFileName;
+	public String getForestFileName() {
+		return forestFileName;
 	}
 
 	public int getTau() {
@@ -226,7 +231,7 @@ public class Forest implements Serializable {
 	}
 
 	public void writeToFile() {
-		writeToFile(defaultFileName);
+		writeToFile(forestFileName);
 	}
 
 	public void writeToFile(String filename) {
@@ -266,6 +271,7 @@ public class Forest implements Serializable {
 					e.printStackTrace();
 				}
 		}
+		forest.forestFileName = filename;
 		return forest;
 	}
 }

+ 8 - 0
src/oram/Metadata.java

@@ -208,6 +208,14 @@ public class Metadata {
 		return defaultForestFileName;
 	}
 
+	public String getDefaultSharesName1() {
+		return defaultForestFileName + ".share1";
+	}
+
+	public String getDefaultSharesName2() {
+		return defaultForestFileName + ".share2";
+	}
+
 	public int getTau() {
 		return tau;
 	}

+ 16 - 0
src/protocols/Party.java

@@ -0,0 +1,16 @@
+package protocols;
+
+public enum Party {
+	Charlie("charlie"), Debbie("debbie"), Eddie("eddie");
+
+	private final String str;
+
+	private Party(String str) {
+		this.str = str;
+	}
+
+	@Override
+	public String toString() {
+		return str;
+	}
+}

+ 75 - 0
src/protocols/Protocol.java

@@ -0,0 +1,75 @@
+package protocols;
+
+import communication.Communication;
+import exceptions.NoSuchPartyException;
+import oram.Forest;
+import oram.Metadata;
+
+public abstract class Protocol {
+	Communication con1, con2;
+
+	/*
+	 * Connections are alphabetized so:
+	 * 
+	 * For Eddie con1 = debbie con2 = charlie
+	 * 
+	 * For Debbie con1 = eddie con2 = charlie
+	 * 
+	 * For Charlie con1 = eddie con2 = debbie
+	 */
+	public Protocol(Communication con1, Communication con2) {
+		this.con1 = con1;
+		this.con2 = con2;
+	}
+
+	private static final boolean ENSURE_SANITY = true;
+
+	public boolean ifSanityCheck() {
+		return ENSURE_SANITY;
+	}
+
+	// Utility function will test for synchrony between the parties.
+	public void sanityCheck() {
+		if (ENSURE_SANITY) {
+
+			// System.out.println("Sanity check");
+			con1.write("sanity");
+			con2.write("sanity");
+
+			if (!con1.readString().equals("sanity")) {
+				System.out.println("Sanity check failed for con1");
+			}
+			if (!con2.readString().equals("sanity")) {
+				System.out.println("Sanity check failed for con2");
+			}
+		}
+	}
+
+	public void run(Party party, String configFile, String forestFile) {
+		Metadata md = new Metadata(configFile);
+		Forest forest = null;
+
+		if (party == Party.Eddie) {
+			if (forestFile == null)
+				forest = Forest.readFromFile(md.getDefaultSharesName1());
+			else
+				forest = Forest.readFromFile(forestFile);
+		} else if (party == Party.Debbie) {
+			if (forestFile == null)
+				forest = Forest.readFromFile(md.getDefaultSharesName2());
+			else
+				forest = Forest.readFromFile(forestFile);
+		} else if (party == Party.Charlie) {
+		} else {
+			throw new NoSuchPartyException(party.toString());
+		}
+
+		run(party, md, forest);
+	}
+
+	/*
+	 * This is mostly just testing code and may need to change for the purpose
+	 * of an actual execution
+	 */
+	public abstract void run(Party party, Metadata md, Forest forest);
+}

+ 16 - 0
src/util/Util.java

@@ -70,4 +70,20 @@ public class Util {
 		for (int i = 0; i < a.length; i++)
 			a[i] = (byte) (a[i] ^ b[i]);
 	}
+	
+	public static void debug(String s) {
+		// only to make Communication.java compile
+	}
+	
+	public static void disp(String s) {
+		// only to make Communication.java compile
+	}
+	
+	public static void error(String s) {
+		// only to make Communication.java compile
+	}
+	
+	public static void error(String s, Exception e) {
+		// only to make Communication.java compile
+	}
 }

+ 11 - 1
test/ui/InitForest.java

@@ -1,12 +1,22 @@
 package ui;
 
+import crypto.OramCrypto;
 import oram.Forest;
+import oram.Metadata;
 
 public class InitForest {
 
 	public static void main(String[] args) {
-		Forest forest = new Forest();
+		Metadata md = new Metadata();
+
+		Forest forest = new Forest(md);
 		forest.writeToFile();
+
+		Forest share1 = forest;
+		Forest share2 = new Forest(md, OramCrypto.sr);
+		share1.setXor(share2);
+		share1.writeToFile(md.getDefaultSharesName1());
+		share2.writeToFile(md.getDefaultSharesName2());
 	}
 
 }

+ 203 - 0
test/ui/TestCLI.java

@@ -0,0 +1,203 @@
+package ui;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.net.InetSocketAddress;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+import communication.Communication;
+import exceptions.NoSuchPartyException;
+import protocols.Party;
+import protocols.Protocol;
+
+public class TestCLI {
+	public static final int DEFAULT_PORT = 8000;
+	public static final String DEFAULT_IP = "localhost";
+
+	public static void main(String[] args) {
+		// Setup command line argument parser
+		Options options = new Options();
+		options.addOption("config", true, "Config file");
+		options.addOption("forest", true, "Forest file");
+		options.addOption("eddie_ip", true, "IP to look for eddie");
+		options.addOption("debbie_ip", true, "IP to look for debbie");
+		options.addOption("protocol", true, "Algorithim to test");
+
+		// Parse the command line arguments
+		CommandLineParser cmdParser = new GnuParser();
+		CommandLine cmd = null;
+		try {
+			cmd = cmdParser.parse(options, args);
+		} catch (ParseException e1) {
+			e1.printStackTrace();
+		}
+
+		String configFile = cmd.getOptionValue("config", "config.yaml");
+
+		String party = null;
+		String[] positionalArgs = cmd.getArgs();
+		if (positionalArgs.length > 0) {
+			party = positionalArgs[0];
+		} else {
+			try {
+				throw new ParseException("No party specified");
+			} catch (ParseException e) {
+				e.printStackTrace();
+			}
+		}
+
+		int extra_port = 1;
+		int eddiePort1 = DEFAULT_PORT;
+		int eddiePort2 = eddiePort1 + extra_port;
+		int debbiePort = eddiePort2 + extra_port;
+
+		String eddieIp = cmd.getOptionValue("eddie_ip", DEFAULT_IP);
+		String debbieIp = cmd.getOptionValue("debbie_ip", DEFAULT_IP);
+
+		//
+		// Class<? extends Protocol> operation = null;
+		// String protocol = cmd.getOptionValue("protocol",
+		// "retrieve").toLowerCase();
+		//
+		// if (protocol.equals("access")) {
+		// operation = Access.class;
+		// } else {
+		// System.out.println("Method " + protocol + " not supported");
+		// System.exit(-1);
+		// }
+		//
+		// Constructor<? extends Protocol> operationCtor = null;
+		// try {
+		// operationCtor = operation.getDeclaredConstructor(Communication.class,
+		// Communication.class);
+		// } catch (NoSuchMethodException | SecurityException e1) {
+		// e1.printStackTrace();
+		// }
+		//
+
+		// For now all logic happens here. Eventually this will get wrapped
+		// up in party specific classes.
+		System.out.println("Starting " + party + "...");
+
+		if (party.equals("eddie")) {
+			Communication debbieCon = new Communication();
+			debbieCon.start(eddiePort1);
+
+			Communication charlieCon = new Communication();
+			charlieCon.start(eddiePort2);
+
+			System.out.println("Waiting to establish connections...");
+			while (debbieCon.getState() != Communication.STATE_CONNECTED)
+				;
+			while (charlieCon.getState() != Communication.STATE_CONNECTED)
+				;
+			System.out.println("Connection established.");
+
+			debbieCon.write("start");
+			charlieCon.write("start");
+			debbieCon.readString();
+			charlieCon.readString();
+
+			// try {
+			// operationCtor.newInstance(debbieCon, charlieCon).run(Party.Eddie,
+			// configFile, null);
+			// } catch (InstantiationException | IllegalAccessException |
+			// IllegalArgumentException
+			// | InvocationTargetException e1) {
+			// e1.printStackTrace();
+			// }
+
+			debbieCon.write("end");
+			charlieCon.write("end");
+			debbieCon.readString();
+			charlieCon.readString();
+
+			debbieCon.stop();
+			charlieCon.stop();
+
+		} else if (party.equals("debbie")) {
+			Communication eddieCon = new Communication();
+			InetSocketAddress eddieAddr = new InetSocketAddress(eddieIp, eddiePort1);
+			eddieCon.connect(eddieAddr);
+
+			Communication charlieCon = new Communication();
+			charlieCon.start(debbiePort);
+
+			System.out.println("Waiting to establish connections...");
+			while (eddieCon.getState() != Communication.STATE_CONNECTED)
+				;
+			while (charlieCon.getState() != Communication.STATE_CONNECTED)
+				;
+			System.out.println("Connection established");
+
+			eddieCon.write("start");
+			charlieCon.write("start");
+			eddieCon.readString();
+			charlieCon.readString();
+
+			// try {
+			// operationCtor.newInstance(eddieCon, charlieCon).run(Party.Debbie,
+			// configFile, null);
+			// } catch (InstantiationException | IllegalAccessException |
+			// IllegalArgumentException
+			// | InvocationTargetException e1) {
+			// e1.printStackTrace();
+			// }
+
+			eddieCon.write("end");
+			charlieCon.write("end");
+			eddieCon.readString();
+			charlieCon.readString();
+
+			eddieCon.stop();
+			charlieCon.stop();
+
+		} else if (party.equals("charlie")) {
+			Communication debbieCon = new Communication();
+			Communication eddieCon = new Communication();
+			InetSocketAddress eddieAddr = new InetSocketAddress(eddieIp, eddiePort2);
+			eddieCon.connect(eddieAddr);
+			InetSocketAddress debbieAddr = new InetSocketAddress(debbieIp, debbiePort);
+			debbieCon.connect(debbieAddr);
+
+			System.out.println("Waiting to establish connections...");
+			while (eddieCon.getState() != Communication.STATE_CONNECTED)
+				;
+			while (debbieCon.getState() != Communication.STATE_CONNECTED)
+				;
+			System.out.println("Connection established");
+
+			eddieCon.write("start");
+			debbieCon.write("start");
+			eddieCon.readString();
+			debbieCon.readString();
+
+			// try {
+			// operationCtor.newInstance(eddieCon, debbieCon).run(Party.Charlie,
+			// configFile, null);
+			// } catch (InstantiationException | IllegalAccessException |
+			// IllegalArgumentException
+			// | InvocationTargetException e1) {
+			// e1.printStackTrace();
+			// }
+
+			eddieCon.write("end");
+			debbieCon.write("end");
+			eddieCon.readString();
+			debbieCon.readString();
+
+			eddieCon.stop();
+			debbieCon.stop();
+
+		} else {
+			throw new NoSuchPartyException(party);
+		}
+
+		System.out.println(party + " exiting...");
+	}
+}