package edu.washington.cs.oneswarm.f2f.dht;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;
import com.google.common.collect.Lists;
import edu.washington.cs.oneswarm.ui.gwt.rpc.CommunityRecord;
import edu.washington.cs.oneswarm.ui.gwt.server.community.CHTGetOp;
import edu.washington.cs.oneswarm.ui.gwt.server.community.CHTPutOp;
import edu.washington.cs.oneswarm.ui.gwt.server.community.CommunityServerManager;
public class CHTClientHTTP implements CHTClientInterface {
private static Logger logger = Logger.getLogger(CHTClientHTTP.class.getName());
Timer batchFlusher;
class PutOp {
public byte[] key;
public byte[] val;
public PutOp(byte[] key, byte[] val) {
this.key = key;
this.val = val;
}
}
class GetOp {
public byte[] key;
CHTCallback callback;
public GetOp(byte[] key, CHTCallback callback) {
this.key = key;
this.callback = callback;
}
}
List<PutOp> pendingPuts = Collections.synchronizedList(new LinkedList<PutOp>());
List<GetOp> pendingGets = Collections.synchronizedList(new LinkedList<GetOp>());
private final CommunityRecord record;
public CHTClientHTTP(String url) {
this(CommunityServerManager.get().getRecordForUrl(url));
}
public CHTClientHTTP(CommunityRecord server) {
this.record = server;
batchFlusher = new Timer("Batch GET/PUT flusher - " + record.getCht_path(), true);
batchFlusher.schedule(new TimerTask() {
@Override
public void run() {
flushPuts();
flushGets();
}
}, 1000, 1000);
}
public CommunityRecord getServerRecord() {
return record;
}
@Override
public void put(byte[] key, byte[] value) throws IOException {
pendingPuts.add(new PutOp(key, value));
logger.fine("CHT put queue length: " + pendingPuts.size());
}
@Override
public void get(byte[] key, CHTCallback callback) {
pendingGets.add(new GetOp(key, callback));
}
public void shutdown() {
logger.fine("Shutting down CHTClientHTTP: " + record);
batchFlusher.cancel();
}
private void flushPuts() {
List<byte[]> keys = Lists.newArrayList();
List<byte[]> values = Lists.newArrayList();
while (!pendingPuts.isEmpty()) {
PutOp op = pendingPuts.remove(0);
if (op == null) {
break;
}
keys.add(op.key);
values.add(op.val);
}
if (keys.size() > 0) {
logger.fine("Sending " + keys.size() + " CHT puts to " + record.getRealUrl());
new CHTPutOp(record, keys, values).start();
}
}
private void flushGets() {
List<byte[]> keys = Lists.newArrayList();
List<CHTCallback> callbacks = Lists.newArrayList();
while (!pendingGets.isEmpty()) {
GetOp op = pendingGets.remove(0);
if (op == null) {
break;
}
keys.add(op.key);
callbacks.add(op.callback);
}
if (keys.size() > 0) {
logger.fine("Sending " + keys.size() + " CHT gets to " + record.getCht_path());
new CHTGetOp(record, keys, callbacks).start();
}
}
}