package org.openqueue.applet1;
import java.net.*;
import java.io.*;
import java.awt.List;
import java.util.StringTokenizer;
import java.util.Vector;
public class OQConversationThread extends Thread {
private DataOutputStream dout;
private DataInputStream din;
private String lineRead;
protected Socket ourSocket;
protected int state;
protected SimpleOQAwareness OQAwareObject; // our applet
protected String paramServer;
protected int paramPort = 8764;
protected String paramUsername;
protected String paramPassword;
protected int paramAutoTopic;
static final int OQState_Offline = 0;
static final int OQState_Connected = 1;
static final int OQState_LoggedIn = 2;
static final int OQState_ReceivingTxn = 3;
static final int OQState_ReceivingTxnStartItem = 4;
static final int OQState_AwaitingAckOK = 5;
public boolean bSessionShouldContinue = true;
public OQConversationThread(SimpleOQAwareness sba, String vserver, int vport, String vusername, String vpassword, int vautoTopic) {
OQAwareObject = sba;
paramServer = vserver;
paramPort = vport;
paramUsername = vusername;
paramPassword = vpassword;
paramAutoTopic= vautoTopic;
}
private void setState(int newState){
int oldState = state;
state = newState;
if (OQAwareObject != null) {
OQAwareObject.fireOQConnectionStateChange( state, oldState );
}
}
private void handleErrorString(String s) {
if (OQAwareObject != null) {
OQAwareObject.fireOQError(s);
System.err.println(s);
}
}
public void run(){
Vector tempMessageIDs = new Vector(20);
Vector tempTopicIDs = new Vector(20);
Vector tempSubjects = new Vector(20);
Vector tempCompleteBodies = new Vector(20);
int thisMessageID = -1; // to make sure we set it
int thisTopicID = -1;
String thisSubject = new String("");
String thisCompleteBody = new String("");
long thisContentSize = 0;
long insertedLines = 0;
boolean subjectSeen = false; // reset for each item/message started
boolean blankSeen = false; // this is the line just before the message body.
StringTokenizer sTokenizer;
String aCmdTag = new String("");
String aCommand = new String("");
int currentTxnID = 0;
setState( OQState_Offline );
try {
try {
ourSocket = new Socket (paramServer, paramPort);
} catch (Exception ex) {
System.out.println ("Exception in opening socket....");
ex.printStackTrace();
handleErrorString( ex.getMessage() );
}
if (ourSocket != null) {
din = new DataInputStream (new BufferedInputStream (ourSocket.getInputStream ()));
dout = new DataOutputStream (new BufferedOutputStream (ourSocket.getOutputStream ()));
while ((bSessionShouldContinue == true) && ((lineRead = din.readLine ()) != null)) {
switch (state) {
case OQState_Offline :
if (lineRead.startsWith("OPENQUEUE")) {
lineRead = din.readLine(); // eat blank line
setState ( OQState_Connected );
dout.writeBytes("LOGIN " + paramUsername + " " + paramPassword + " mode=alive\n\n");
dout.flush();
} else {
handleErrorString ("Error - expected OQ greeting from server.");
// XXX handle error...
}
break;
case OQState_Connected :
if (lineRead.startsWith("200 -")) {
while ((lineRead.equals("")) == false) {
lineRead = din.readLine(); // eat lines telling how many messages each topic has
}
setState ( OQState_LoggedIn );
if (paramAutoTopic != -1) {
dout.writeBytes("!a1 SUBSCRIBE #" + paramAutoTopic + "\n\n");
}
dout.writeBytes("!a2 UPDATES ON\n\n");
dout.flush();
} else {
handleErrorString ("Error - expected OQ greeting from server.");
// XXX handle error...
}
break;
case OQState_LoggedIn :
sTokenizer = new StringTokenizer (lineRead, " =");
try {
aCommand = "";
aCmdTag = "";
if (sTokenizer.hasMoreTokens()) aCmdTag = sTokenizer.nextToken();
if (sTokenizer.hasMoreTokens()) aCommand = sTokenizer.nextToken();
// if it's an unexpected Command (e.g., UPDATE), we see it as aCmdTag.
if (aCmdTag.toUpperCase().equals("UPDATE")) {
// figure out txn number.
if (sTokenizer.hasMoreTokens()) currentTxnID = Integer.parseInt(sTokenizer.nextToken());
tempMessageIDs.removeAllElements();
tempTopicIDs.removeAllElements();
tempSubjects.removeAllElements();
tempCompleteBodies.removeAllElements();
setState( OQState_ReceivingTxn );
}
} catch (Exception ex) {
System.out.println ("Exception while handling command...");
ex.printStackTrace();
}
break;
case OQState_ReceivingTxn :
if (lineRead.equals("ENDUPDATE")) {
dout.writeBytes("!a ACK " + Integer.toString( currentTxnID ) + "\r\n\r\n");
dout.flush();
setState(OQState_AwaitingAckOK);
}
if (lineRead.startsWith("START ITEM")) {
// parse out topic and message numbers...
int topicStart = lineRead.indexOf("#");
if (topicStart > 0) {
int topicEnd = lineRead.indexOf(" ", topicStart);
try {
String tempS = new String("");
tempS = lineRead.substring(topicStart+1, topicEnd);
thisTopicID = Integer.parseInt(tempS);
tempS = new String(lineRead.substring(topicEnd+1, lineRead.length()));
thisMessageID = Integer.parseInt(tempS);
thisMessageID = thisMessageID+1;
thisMessageID = thisMessageID-1;
} catch (Exception ex) {
handleErrorString("xxx - a topic id or a message id was not a valid number.");
}
blankSeen = false;
subjectSeen = false;
thisSubject = "";
thisCompleteBody = "";
thisContentSize = -1;
insertedLines = 0;
setState(OQState_ReceivingTxnStartItem);
} else {
handleErrorString("xxx - could not find proper topic number in an update.");
}
}
break;
case OQState_ReceivingTxnStartItem :
if (blankSeen == false) {
//first, add header to complete body.
if (insertedLines != 0) { // at least one line before us, so add a crlf.
thisCompleteBody = thisCompleteBody + "\r\n" + lineRead;
} else {
thisCompleteBody = thisCompleteBody + lineRead;
}
insertedLines++;
//next, process the elements of the header, as needed.
if (lineRead.toUpperCase().startsWith("SUBJECT:")) {
thisSubject = lineRead.substring(9);
}
if (lineRead.toUpperCase().startsWith("CONTENT-LENGTH:")) {
thisContentSize = Long.parseLong(lineRead.substring(16));
}
if (lineRead.equals("")) {
blankSeen = true;
insertedLines = 0; // reset for content where we have no content-length header.
thisCompleteBody = thisCompleteBody + "\r\n";
if (thisContentSize != -1) {
// read in contents all at once here!!
if (thisContentSize < 2147000000) {
byte[] buffer = new byte[(int)thisContentSize];
din.readFully(buffer);
String aTempStr = new String(buffer, 0);
thisCompleteBody = thisCompleteBody + aTempStr;
// now read the crlf at the end of all this content.
aTempStr = din.readLine ();
} else {
handleErrorString("xxx - um, we can't really handle messages larger than 2 billion bytes yet.");
}
}
}
}
if (lineRead.equals("END ITEM")) {
//store this in our vectors.
tempMessageIDs.addElement(new Integer(thisMessageID));
tempTopicIDs.addElement(new Integer(thisTopicID));
tempSubjects.addElement(new String(thisSubject));
tempCompleteBodies.addElement(new String(thisCompleteBody));
setState(OQState_ReceivingTxn);
} else {
// This is for where we haven't got a COntent-Length header.
if ((thisContentSize == -1) && (blankSeen == true)) {
// add data to messageBody. insert CRLF.
if (insertedLines != 0) { // at least one line before us, so add a crlf.
thisCompleteBody = thisCompleteBody + "\r\n" + lineRead;
} else {
thisCompleteBody = thisCompleteBody + lineRead;
}
insertedLines++;
}
}
break;
case OQState_AwaitingAckOK :
if (lineRead.startsWith("!a 200") ){
//Yay! Transaction is complete. Now report the messages to our OQAwareObject.
if (OQAwareObject != null) {
int numMessages = tempMessageIDs.size();
for (int j=0; j<numMessages; j++) {
thisTopicID = ((Integer) tempTopicIDs.elementAt(j)).intValue();
thisMessageID = ((Integer) tempMessageIDs.elementAt(j)).intValue();
thisSubject = (String) tempSubjects.elementAt(j);
thisCompleteBody = (String) tempCompleteBodies.elementAt(j);
OQAwareObject.fireOQMessageReceived(thisTopicID, thisMessageID, thisSubject, thisCompleteBody);
}
}
setState( OQState_LoggedIn );
}
break;
} // end of switch (state)
} // while
} // if socket != null
} catch (IOException ex) {
System.out.println ("Exception in while loop....");
ex.printStackTrace();
handleErrorString( ex.getMessage() );
} finally {
try {
setState ( OQState_Offline );
if (ourSocket != null) { ourSocket.close (); }
} catch (Exception ex) {
System.out.println ("Exception in closing socket....");
ex.printStackTrace();
handleErrorString( ex.getMessage() );
}
// System.out.println ("Connection is gone.");
}
}
}