package net.varkhan.serv.p2p.message.dispatch;
import net.varkhan.serv.p2p.connect.PeerAddress;
import net.varkhan.serv.p2p.message.MesgEnvelope;
import net.varkhan.serv.p2p.message.MesgPayload;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;
/**
* <b></b>.
* <p/>
*
* @author varkhan
* @date 5/30/11
* @time 7:54 AM
*/
public class QueuedReceiver implements MesgReceiver {
private volatile long maxtime;
private final long starttime;
private final long maxcount;
private volatile boolean cancelled=false;
private final AtomicLong received =new AtomicLong();
private final ConcurrentLinkedQueue<MesgEnvelope> queue =new ConcurrentLinkedQueue<MesgEnvelope>();
/**
* Creates a reply handler for CALL message that will receive and store a single REPL message
*
* @param maxtime the maximum time (in milliseconds) the hanler will wait for a reply before being released
* @throws IllegalArgumentException if the value of timeout is negative
*/
public QueuedReceiver(long maxtime, long maxcount) throws IllegalArgumentException {
if(maxtime<=0) throw new IllegalArgumentException("Max time must be positive or zero");
this.maxtime = maxtime;
if(maxcount<=0) throw new IllegalArgumentException("Max count must be positive or zero");
this.maxcount = maxcount;
this.starttime = System.currentTimeMillis();
}
public void receive(PeerAddress src, PeerAddress dst, String method, MesgPayload message) {
this.queue.offer(new Envelope(src,dst,method,message));
this.received.incrementAndGet();
this.notifyAll();
}
/**
* Receive a message.
* <p/>
* This method will block until either a message was received, or the operation failed, timed out, or was cancelled
*
* @param timeout the maximum time to wait in milliseconds (or {@code 0} to wait forever)
* @return an envelope containing the information received, or {@code null} if nothing was received
* @throws IllegalArgumentException if the value of timeout is negative
* @see #receive()
*/
public MesgEnvelope receive(long timeout) throws IllegalArgumentException {
if(timeout<=0) throw new IllegalArgumentException("Timeout must be positive or zero");
if(timeout==0) {
MesgEnvelope env;
// The order matters here: we want to retrieve before checking aborts
while((env=queue.poll())==null && !finished()) try {
// We need synchronization to allow us to wait()
synchronized(this) { this.wait(); }
} catch (InterruptedException e) {
// Ignore spurious interrupts
}
return env;
}
else {
long time = System.currentTimeMillis();
MesgEnvelope env;
// The order matters here: we want to retrieve before checking aborts
while((env=queue.poll())==null && !finished() && timeout>0) try {
// We need synchronization to allow us to wait()
synchronized(this) { this.wait(timeout); }
} catch (InterruptedException e) {
// Ignore spurious interrupts, but update the timer
long t = System.currentTimeMillis();
timeout = timeout+time-t;
time = t;
}
return env;
}
}
/**
* Indicate whether a single message has been received.
* <p/>
* This method return immediately.
*
* @return an envelope containing the information received, or {@code null} if nothing was received
* @see #receive(long)
*/
public MesgEnvelope receive() { return queue.poll(); }
/**
* The number of messages received.
*
* @return the total number of messages received so far
* @see #receive(long)
* @see #receive()
*/
public long received() { return received.get(); }
public void cancel() {
this.cancelled = true;
this.notifyAll();
}
public boolean isCancelled() {
return cancelled;
}
public void release() {
this.cancelled = true;
this.notifyAll();
}
public boolean finished() { return received.get()>=maxcount || cancelled || maxtime<(System.currentTimeMillis()-starttime); }
/**
* Set the maximum amount of time the handler will wait for a reply
*
* @param timeout the maximum reply time, in milliseconds
*/
public void setMaxDelay(long timeout) {
this.maxtime= timeout;
}
public long getMaxDelay() { return maxtime; }
public long getMaxCount() { return maxcount; }
/**
* <b></b>.
* <p/>
*
* @author varkhan
* @date 5/30/11
* @time 8:21 AM
*/
public static class Envelope implements MesgEnvelope {
private final PeerAddress src;
private final PeerAddress dst;
private final String method;
private final MesgPayload message;
public Envelope(PeerAddress src, PeerAddress dst, String method, MesgPayload message) {
this.src=src;
this.dst=dst;
this.method=method;
this.message=message;
}
public PeerAddress src() {
return src;
}
public PeerAddress dst() {
return dst;
}
public String method() {
return method;
}
public MesgPayload message() {
return message;
}
}
}