package com.neverwinterdp.sparkngin.http;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.QueryStringEncoder;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import com.neverwinterdp.message.Message;
import com.neverwinterdp.netty.http.client.AsyncHttpClient;
import com.neverwinterdp.netty.http.client.ResponseHandler;
import com.neverwinterdp.sparkngin.Ack;
abstract public class AbstractHttpSparknginClient {
private String path = "/message/json" ;
private AsyncHttpClient client ;
private LinkedHashMap<String, Message> waitingAckMessages ;
private int bufferSize ;
private int sendCount = 0;
private int errorCount = 0;
public AbstractHttpSparknginClient(String host, int port, int bufferSize, boolean connect) throws Exception {
client = new AsyncHttpClient(host, port, new MessageResponseHandler(), connect) ;
this.bufferSize = bufferSize ;
waitingAckMessages = new LinkedHashMap<String, Message>() ;
}
public boolean isConnected() { return client.isConnected() ; }
public void setNotConnected() { client.setNotConnected(); }
public boolean connect() throws Exception {
return client.connect();
}
public boolean connect(long timeout, long tryPeriod) throws Exception {
return client.connect(timeout, tryPeriod);
}
public void setPath(String path) { this.path = path ; }
public int getSendCount() { return this.sendCount ; }
public int getErrorCount() { return errorCount ;}
public void sendGet(Message message, long timeout) throws Exception {
synchronized(waitingAckMessages) {
if(waitingAckMessages.size() >= bufferSize) {
waitingAckMessages.wait(timeout);
if(waitingAckMessages.size() >= bufferSize) {
throw new TimeoutException("fail to send the message in " + timeout + "ms") ;
}
}
QueryStringEncoder encoder = new QueryStringEncoder(path);
encoder.addParam("data", toStringData(message));
client.get(encoder.toString());
sendCount++ ;
String messageId = message.getHeader().getKey() ;
waitingAckMessages.put(messageId, message) ;
}
}
public void sendPost(Message message, long timeout) throws Exception {
synchronized(waitingAckMessages) {
if(waitingAckMessages.size() >= bufferSize) {
waitingAckMessages.wait(timeout);
if(waitingAckMessages.size() >= bufferSize) {
throw new TimeoutException("fail to send the message in " + timeout + "ms") ;
}
}
client.post(path, toBinData(message));
sendCount++ ;
String messageId = message.getHeader().getKey() ;
waitingAckMessages.put(messageId, message) ;
}
}
public void onFailedMessage(Ack ack, Message message) {
errorCount++ ;
System.out.println("Failed message: " + ack.getMessageId() + ", message = " + ack.getMessage());
}
public Map<String, Message> getWaitingMessages() {
return this.waitingAckMessages ;
}
public void waitAndClose(long waitTime) throws InterruptedException {
if(waitingAckMessages.size() > 0) {
synchronized(waitingAckMessages) {
long stopTime = System.currentTimeMillis() + waitTime ;
while(stopTime > System.currentTimeMillis() && waitingAckMessages.size() > 0) {
long timeToWait = stopTime - System.currentTimeMillis() ;
waitingAckMessages.wait(timeToWait);
}
}
}
close();
}
public void close() {
if(waitingAckMessages.size() > 0) {
System.err.println("There are " + waitingAckMessages.size() + " messages waitting for ack") ;
}
client.close();
}
abstract protected byte[] toBinData(Message message) ;
abstract protected String toStringData(Message message) ;
abstract protected Ack toAck(HttpContent content) ;
protected class MessageResponseHandler implements ResponseHandler {
public void onResponse(HttpResponse response) {
if(response instanceof HttpContent) {
Ack ack = toAck((HttpContent) response) ;
String messageId = (String) ack.getMessageId() ;
Message message = waitingAckMessages.get(messageId) ;
if(!Ack.Status.OK.equals(ack.getStatus())) {
onFailedMessage(ack, message) ;
}
synchronized(waitingAckMessages) {
waitingAckMessages.remove(messageId) ;
waitingAckMessages.notify() ;
}
}
}
}
}