package com.techq.available.quorum.handler;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.print.attribute.standard.Finishings;
import org.apache.jute.Record;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.forest.ape.server.Type;
import com.techq.available.AvailableConfig;
import com.techq.available.connector.impl.DataCnxManager;
import com.techq.available.data.BasicPacket;
import com.techq.available.quorum.DebugConfig;
import com.techq.available.quorum.Election;
import com.techq.available.quorum.Message;
import com.techq.available.quorum.Notification;
/**
*
* @author CHQ
* 2012-2-3
*/
public class LearnerHandler extends Thread {
private static final Logger LOG = LoggerFactory.getLogger(LearnerHandler.class);
LinkedBlockingQueue<BasicPacket> sendqueue = new LinkedBlockingQueue<BasicPacket>();
LinkedBlockingQueue<BasicPacket> recvqueue = new LinkedBlockingQueue<BasicPacket>();
final Socket sock;
Leader leader;
volatile long lastTick;
volatile long preAck;
DataCnxManager.MessageReceiver recvThread;
DataCnxManager.MessageSender sendThread;
ProcessorWorker worker;
public LearnerHandler(Socket sock, Leader leader, long id) throws IOException {
super("LearnerHandler-" + sock.getRemoteSocketAddress());
this.sock = sock;
lastTick = leader.tick;
this.leader = leader;
this.leader.addLearnerHandler(this);
recvThread = new DataCnxManager.MessageReceiver(this.sock, recvqueue, id);
sendThread = new DataCnxManager.MessageSender(this.sock, sendqueue, id);
worker = new ProcessorWorker(id);
}
public synchronized void shutdown() {
try {
if (recvThread.isAlive())
recvThread.halt();
if (sendThread.isAlive())
sendThread.halt();
if (worker.isAlive())
worker.finish();
if (this.sock != null && this.sock.isConnected()) {
this.sock.close();
}
if (this.isAlive())
this.interrupt();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace(System.out);
}
leader.removeLearnerHandler(this);
}
@Override
public void run() {
worker.start();
recvThread.start();
sendThread.start();
try {
//wait for them
worker.join();
recvThread.join();
sendThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace(System.out);
shutdown();
}
}
public boolean sync(long leadTick) {
boolean res = this.isAlive() && lastTick > leadTick - AvailableConfig.syncTick;
if (res == true)
LOG.debug("sync ok, last tick:" + lastTick + ", tick:" + leadTick);
return res;
}
public BasicPacket pollRecvQueue(long time, TimeUnit unit) throws InterruptedException
{
return recvqueue.poll(time, unit);
}
public void toSend(BasicPacket n)
{
sendqueue.offer(n);
}
public void correct(long tick) {
if (tick != lastTick && lastTick > preAck)
lastTick = tick;
}
/*class MessageReceiver extends Thread {
volatile boolean running = true;
@Override
public void run() {
DataInputStream in = null;
try {
in = new DataInputStream(sock.getInputStream());
} catch (IOException e1) {
e1.printStackTrace();
this.halt();
return;
}
while(running) {
try {
byte requestBytes[];
if (DebugConfig.debug)
requestBytes = new byte[Message.DEBUG_DEFAULT_SIZE];
else
requestBytes = new byte[Message.DEFAULT_SIZE];
in.read(requestBytes);
ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes);
Notification n = new Notification();
int type = requestBuffer.getInt();
int state = requestBuffer.getInt();
long leader = requestBuffer.getLong();
long zxid = requestBuffer.getLong();
long clock = requestBuffer.getLong();
long from = requestBuffer.getLong();
long sid = requestBuffer.getLong();
n.setType(Notification.getTypeByInt(type));
n.setState(Notification.getStateByInt(state));
n.setLeader(leader);
n.setZxid(zxid);
n.setLogicalClock(clock);
n.setFrom(from);
n.setSid(sid);
recvqueue.offer(n);
} catch (IOException e) {
LOG.error("read time", e);
this.halt();
shutdown();
}
}
}
public void halt() {
if (!running)
return;
running = false;
this.interrupt();
}
}
class MessageSender extends Thread {
volatile boolean running = true;
@Override
public void run() {
DataOutputStream dos = null;
try {
dos = new DataOutputStream(sock.getOutputStream());
} catch (IOException e1) {
e1.printStackTrace(System.out);
this.halt();
shutdown();
return;
}
while(running) {
try {
Notification m = sendqueue.poll(200, TimeUnit.MILLISECONDS);
if (m == null)
continue;
LOG.info("send:" + m);
byte requestBytes[];
if (DebugConfig.debug)
requestBytes = new byte[Message.DEBUG_DEFAULT_SIZE];
else
requestBytes = new byte[Message.DEFAULT_SIZE];
ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes);
requestBuffer.clear();
requestBuffer.putInt(m.getType().ordinal());
requestBuffer.putInt(m.getState().ordinal());
requestBuffer.putLong(m.getLeader());
requestBuffer.putLong(m.getZxid());
requestBuffer.putLong(m.getLogicalClock());
requestBuffer.putLong(m.getFrom());//from which peer
requestBuffer.putLong(m.getSid());//from which peer
if (DebugConfig.debug)
requestBuffer.putInt(m.getSeq());
try {
dos.write(requestBytes);
} catch (IOException e) {
LOG.error("write exception", e);
this.halt();
shutdown();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void halt() {
if (!running)
return;
running = false;
this.interrupt();
}
}*/
class ProcessorWorker extends Thread {
volatile boolean running = true;
ProcessorWorker(long id) {
super("ProcessorWorker[myid=" + id + "]");
}
public void run() {
while(running) {
BasicPacket n = null;
try {
n = recvqueue.poll(AvailableConfig.pollTimeout, AvailableConfig.pollTimeUnit);
} catch (InterruptedException e) {
LOG.error("this should not be happened when responsing to follower", e);
}
if (n == null || n != null && !leader.followers.contains(n.getFrom())) {
if (n != null) {
LOG.warn("rubbish message :" + n);
}
continue;
}
LOG.trace("recv msg:" + n);
switch(n.getType()) {
case Type.PING:
LOG.debug("update sid:" + n.getFrom() + ", now its tick:" + lastTick);
preAck = lastTick;
lastTick = leader.tick;
n.setType(Type.ACK);
n.setFrom(leader.leaderId);
LOG.debug("send msg:" + n);
sendqueue.offer(n);
break;
case Type.AGREEMENT:
synchronized (leader.quorums) {
leader.quorums.add(n.getFrom());
}
n.setType(Type.CONFIRM);
n.setFrom(leader.leaderId);
LOG.debug("send msg:" + n);
sendqueue.offer(n);
break;
case Type.DATA:
lastTick = leader.tick;
LOG.debug("recv type:DATA myid:" + leader.leaderId);
leader.addPackets(n);
break;
default:
LOG.warn("rubbish message :" + n);
break;
}
}
}
public void finish() {
if (!running)
return;
running = false;
LOG.debug("halt ProcessorWorker");
this.interrupt();
}
}
}