package qa.qcri.aidr.predict.communication;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date;
import org.apache.log4j.Logger;
import qa.qcri.aidr.predict.common.Event;
/**
* OutputWorker maintains a persistent connection to a consumer of classified
* documents.
*
* @author jrogstadius
*/
public class HttpOutputWorker {
/*
* This is a bit overly complicated, but Output workers need input data from
* the client to be validly created (an ontology specification). Waiting for
* the client data is a blocking operation, so the object creation is runs
* in a separate thread.
*/
private static Logger logger = Logger.getLogger(HttpOutputWorker.class);
public static class WorkerFactory implements Runnable {
private Socket socket;
public void initialize(Socket s) {
socket = s;
}
@Override
public void run() {
OutputFilter o = getFilter(socket);
if (o == null)
return;
HttpOutputWorker worker = new HttpOutputWorker(socket, o);
HttpOutputWorkerIndex.getInstance().addWorker(o.crisisCode, worker);
socket = null;
}
OutputFilter getFilter(Socket s) {
/*
* Example of a HTTP POST request sent from Google Chrome, with
* three form fields at the bottom:
*
* POST / HTTP/1.1 Host: localhost:4321 Connection: keep-alive
* Content-Length: 53 Cache-Control: max-age=0 Accept:
* text/html,application
* /xhtml+xml,application/xml;q=0.9,*FRONTSLASH*;q=0.8 Origin: null
* User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64)
* AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.152
* Safari/537.22 Content-Type: application/x-www-form-urlencoded
* Accept-Encoding: gzip,deflate,sdch Accept-Language:
* en-US,en;q=0.8 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
*
* username=jakob&password=pwd&ontology=3
*/
logger.info("Waiting for POST data");
// DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
// conn.bind(ser.accept(), new BasicHttpParams());
// HttpRequest request = conn.receiveRequestHeader();
// conn.receiveRequestEntity((HttpEntityEnclosingRequest)request);
// HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
// System.out.println(EntityUtils.toString(entity));
//
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(
s.getInputStream()));
} catch (IOException e) {
logger.error("IOException while trying to open input stream");
return null;
}
Date t0 = new Date();
try {
while ((new Date().getTime() - t0.getTime() < 10000)) {
while (!in.ready()
&& (new Date().getTime() - t0.getTime() < 2000)) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
logger.warn("Sleep interrupted while waiting for POST data");
}
}
String contentLengthKeyWord = "Content-Length";
String CRLF ="\r\n\r\n";
char[] cbuffer = new char[4000];
in.read(cbuffer);
String input = new String(cbuffer);
if(input.indexOf(contentLengthKeyWord) > -1){
int size = getContentSize(input);
int pos = input.indexOf(CRLF);
String strTemp = input.substring(pos);
if(size == strTemp.trim().length()){
input = strTemp;
}
}
logger.info("Recieved POST data: " + input);
return OutputFilter.fromJson(input);
}
} catch (IOException e) {
logger.warn("IOException while waiting for POST data");
}
return null;
}
}
static int connectionID = 0;
public final OutputFilter filter;
public Event<Object> onConnectionClosed = new Event<Object>();
private int connectionInstanceID;
private Socket client;
private PrintWriter clientStream = null;
Integer pushCount = 0;
private HttpOutputWorker(Socket s, OutputFilter o) {
connectionInstanceID = connectionID++;
filter = o;
client = s;
logger.info("Creating new OutputWorker (" + connectionInstanceID
+ ") for event " + o.crisisID);
try {
clientStream = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
logger.warn("Could not get output stream for worker "
+ connectionInstanceID);
}
}
public void push(String doc) {
if (!clientStream.checkError()) {
logger.info("Writing to client: " + doc);
clientStream.println(doc);
}
if (clientStream.checkError()) {
// Inform subscribers that the connection has closed
logger.warn("Could not write to client, closing connection");
close();
}
}
boolean isClosed = false;
public void close() {
if (!client.isClosed())
try {
client.close();
} catch (IOException e) {
logger.warn("IOException while closing client connection");
}
if (!isClosed)
onConnectionClosed.fire(this, null);
isClosed = true;
}
@Override
protected void finalize() {
close();
onConnectionClosed.dispose();
}
private static int getContentSize(String input ) throws IOException {
int size = -1;
String contentLengthKeyWord = "Content-Length";
int contentSizeStart = input.indexOf(contentLengthKeyWord) + ( contentLengthKeyWord.length() + 1);
String fromContentSize = input.substring(contentSizeStart).trim();
int contentSizeEnd = fromContentSize.indexOf("\r\n");
String contentSize = fromContentSize.substring(0,contentSizeEnd);
if(isNumericValue(contentSize)){
size = Integer.parseInt(contentSize);
}
return size;
}
private static boolean isNumericValue(String value){
try
{
Integer.parseInt(value);
}
catch(NumberFormatException nfe)
{
return false;
}
return true;
}
}