package se.chalmers.gdcn.utils;
import java.io.Serializable;
import java.util.*;
/**
* Created by Leif on 2014-04-16.
*
* Fully serializable timer. Creates runnable that can update the thread.
*/
public abstract class SerializableTimer<E> implements Serializable {
private final long UPDATE_TIME;
private final PriorityQueue<Timeout<E>> queue = new PriorityQueue<>();
/**
* @param updateTime Number of Milliseconds between check queue
*/
public SerializableTimer(long updateTime) {
UPDATE_TIME = updateTime;
}
/**
* Clock that updates this timer. This class must be Serializable which {@link java.util.Timer} isn't.
* @return Runnable
*/
public final Runnable createUpdater(){
return new Runnable() {
@Override
public void run() {
Timer timer = new Timer(true);
timer.schedule(new TimerTask() {
@Override
public void run() {
update();
}
}, UPDATE_TIME/2, UPDATE_TIME);
}
};
}
/**
* @param timer Timer to be resumed
* @return Daemon thread that runs the timer
*/
public static Thread resume(SerializableTimer timer){
Thread timerThread = new Thread(timer.createUpdater());
timerThread.setDaemon(true);
timerThread.start();
return timerThread;
}
/**
* Add timeout. Will call {@link se.chalmers.gdcn.utils.SerializableTimer#handleTimeout(E)} after specified time.
* @param element element
* @param date absolute date when <code>handleTimeout()</code> will be called
*/
public final synchronized void add(E element, Date date){
Timeout<E> timeout = new Timeout<>(element, date);
queue.add(timeout);
}
/**
* Remove element from queue so that no timeout is called.
* @param element element
* @return if an element was removed.
*/
public final synchronized boolean remove(E element){
//Should work since Timeout equals only depend on element
Timeout<E> timeout = new Timeout<>(element, null);
return queue.remove(timeout);
}
/**
* @param element Element to reset
* @param date Future date
* @return if element was removed before adding
*/
public final synchronized boolean reset(E element, Date date){
boolean removed = false;
if(remove(element)){
removed = true;
}
add(element, date);
return removed;
}
/**
* Called by clock to check the queue.
*/
private synchronized void update(){
final Date currentTime = new Date();
if(queue.peek()==null){
//Queue empty, ignore
return;
}
while(queue.peek()!=null && queue.peek().getDate().compareTo(currentTime) < 0){
Timeout<E> outdated = queue.remove();
handleTimeout(outdated.element);
}
}
/**
* This element was set to timeout at this time. Handle in subclass.
* @param element element
*/
protected abstract void handleTimeout(E element);
private static class Timeout<E> implements Serializable, Comparable<Timeout>{
private final Date date;
private final E element;
private Timeout(E element, Date date) {
this.date = date;
this.element = element;
}
public E getElement() {
return element;
}
public Date getDate() {
return date;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Timeout)) return false;
Timeout timeout = (Timeout) o;
if (!element.equals(timeout.element)) return false;
return true;
}
@Override
public int hashCode() {
return element.hashCode();
}
@Override
public int compareTo(Timeout replicaTimeout) {
if(replicaTimeout==null){
return 1;
}
return date.compareTo(replicaTimeout.date);
}
}
}