package net.varkhan.serv.p2p.message.dispatch;
import net.varkhan.base.management.report.JMXAverageMonitorReport;
import net.varkhan.serv.p2p.connect.PeerAddress;
import net.varkhan.serv.p2p.message.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicLong;
/**
* <b></b>.
* <p/>
*
* @author varkhan
* @date 5/29/11
* @time 11:01 AM
*/
public class AsyncDispatcher implements MesgDispatcher {
private final AtomicLong nextSequence=new AtomicLong();
private final ConcurrentMap<Long,MesgReceiver> registry =new ConcurrentHashMap<Long,MesgReceiver>();
private final ConcurrentMap<String,MesgAction> symbols =new ConcurrentHashMap<String,MesgAction>();
private final Executor executor;
private final JMXAverageMonitorReport stats;
private Thread thread;
private long period;
public AsyncDispatcher(Executor executor, JMXAverageMonitorReport stats, long period) {
this.executor=executor;
this.stats=stats;
this.period=period;
start();
}
public void addMethod(String key, MesgAction value) {
symbols.put(key, value);
}
public MesgAction getMethod(String key) {
return symbols.get(key);
}
public boolean hasMethod(String key) {
return symbols.containsKey(key);
}
public void delMethod(String key) {
symbols.remove(key);
}
public Set<String> methods() {
return symbols.keySet();
}
public long register(MesgReceiver handler) {
long sequence = nextSequence.incrementAndGet();
registry.put(sequence, handler);
return sequence;
}
public void unregister(long sequence) {
MesgReceiver receiver = registry.remove(sequence);
if(receiver!=null) receiver.release();
}
public void call(PeerAddress src, PeerAddress dst, String method, MesgPayload call, MesgPayload repl, long sequence, MesgSender send) {
MesgAction m = symbols.get(method);
if(m==null) return;
executor.execute(new TaskRunner(src,dst,method,m,call,repl,sequence,send));
}
public void repl(PeerAddress src, PeerAddress dst, String method, MesgPayload repl, long sequence) {
MesgReceiver receiver = registry.get(sequence);
if(receiver==null) return;
receiver.receive(src, dst, method, repl);
if(receiver.finished()) {
// Ensure we don't have double releases
receiver = registry.remove(sequence);
if(receiver!=null) receiver.release();
}
}
private static final AtomicLong threadNumber=new AtomicLong();
public void start() {
if(thread!=null) return;
thread = new TimeoutCleaner();
thread.setDaemon(true);
thread.setName(AsyncDispatcher.class.getSimpleName()+"-"+threadNumber.incrementAndGet());
thread.start();
}
public void stop() { stop(period); }
public void stop(long timeout) {
Thread t = thread;
if(t==null) return;
thread = null;
t.interrupt();
try { t.join(timeout); }
catch(InterruptedException e) { /* ignore */ }
}
private final class TimeoutCleaner extends Thread {
public void run() {
while(thread!=null) {
long time = System.currentTimeMillis();
// Clean up finished receivers
for(Iterator<MesgReceiver> iterator=registry.values().iterator();iterator.hasNext();) {
MesgReceiver r=iterator.next();
try {
if(r.finished()) iterator.remove();
}
catch(Throwable t) {
// Failing receivers are forcibly removed
iterator.remove();
}
}
long elapsed = System.currentTimeMillis()-time;
if(elapsed<period) try { Thread.sleep(period-elapsed); }
catch(InterruptedException e) { /* ignore */ }
}
}
}
private final class TaskRunner implements Runnable {
private PeerAddress src;
private PeerAddress dst;
private String name;
private MesgAction impl;
private MesgPayload call;
private MesgPayload repl;
private long sequence;
private MesgSender send;
public TaskRunner(PeerAddress src, PeerAddress dst, String name, MesgAction impl, MesgPayload call, MesgPayload repl, long sequence, MesgSender send) {
this.src=src;
this.dst=dst;
//To change body of created methods use File | Settings | File Templates.
this.name=name;
this.impl=impl;
this.call=call;
this.repl=repl;
this.sequence=sequence;
this.send=send;
}
public void run() {
try {
impl.invoke(src, dst, name, call, repl);
}
finally {
try {
send.send(src, dst, name, repl, sequence);
}
catch(Throwable t) {
stats.inc("Dispatcher.Call.error["+t.getClass().getSimpleName()+"]");
}
}
}
}
}