package org.limewire.concurrent;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Collections;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.limewire.listener.EventListener;
import org.limewire.service.ErrorService;
/**
* An extension for {@link Timer}, allowing you to schedule a {@link Runnable}
* task instead of scheduling a {@link TimerTask}.
*
* This also exposes all the functionality of a {@link ScheduledListeningExecutorService}.
*/
public class SimpleTimer extends AbstractListeningExecutorService implements
ScheduledListeningExecutorService {
/** The underlying Timer of this SimpleTimer. */
private final Timer TIMER;
/** Whether or not we actively cancelled the timer. */
private volatile boolean cancelled = false;
/**
* Creates a new active SimpleTimer.
*
* @param isDaemon true if this' thread should be a daemon.
*/
public SimpleTimer(String name, boolean isDaemon) {
TIMER = new Timer(name, isDaemon);
}
/**
* Creates a new active SimpleTimer.
*
* @param isDaemon true if this' thread should be a daemon.
*/
public SimpleTimer(boolean isDaemon) {
TIMER = new Timer(isDaemon);
}
public ScheduledListeningFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
ListeningScheduledTimerTask<?> future = new ListeningScheduledTimerTask<Object>(command, 0);
scheduleInternal(future, unit.toMillis(delay), 0, false);
return future;
}
public <V> ScheduledListeningFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
ListeningScheduledTimerTask<V> future = new ListeningScheduledTimerTask<V>(callable, 0);
scheduleInternal(future, unit.toMillis(delay), 0, false);
return future;
}
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period,
TimeUnit unit) {
ListeningScheduledTimerTask<?> future = new ListeningScheduledTimerTask<Object>(command, period);
scheduleInternal(future, unit.toMillis(initialDelay), unit.toMillis(period), true);
return future;
}
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
long delay, TimeUnit unit) {
ListeningScheduledTimerTask<?> future = new ListeningScheduledTimerTask<Object>(command, delay);
scheduleInternal(future, unit.toMillis(initialDelay), unit.toMillis(delay), false);
return future;
}
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
throw new UnsupportedOperationException();
}
public boolean isShutdown() {
return cancelled;
}
public boolean isTerminated() {
return cancelled;
}
public void shutdown() {
cancelled = true;
TIMER.cancel();
}
public List<Runnable> shutdownNow() {
shutdown();
return Collections.emptyList();
}
// Does not use ScheduledTimerTask so as to avoid creating the Future.
public void execute(final Runnable command) {
TimerTask tt = new TimerTask() {
@Override
public void run() {
try {
command.run();
} catch (Throwable t) {
ErrorService.error(t);
}
}
};
scheduleInternal(tt, 0, 0, false);
}
/** Schedules the task as necessary. */
private void scheduleInternal(TimerTask task, long delay, long period, boolean fixedRate) {
try {
if (period == 0) {
if (fixedRate) {
throw new IllegalArgumentException("cannot support 0 period w/ fixedRate");
} else {
TIMER.schedule(task, delay);
}
} else {
if (fixedRate) {
TIMER.scheduleAtFixedRate(task, delay, period);
} else {
TIMER.schedule(task, delay, period);
}
}
} catch (IllegalStateException ise) {
if (cancelled)
throw ise;
}
}
/** A TimerTask that delegates to a ListeningRunnableFuture for a ListeningScheduledFuture. */
private static class ListeningScheduledTimerTask<V> extends TimerTask implements
ScheduledListeningFuture<V> {
private final RunnableListeningFuture<V> task;
public ListeningScheduledTimerTask(final Runnable r, long period) {
task = new ListeningResetableFutureTask<V>(new Runnable() {
public void run() {
try {
r.run();
} catch(RuntimeException e) {
ErrorService.error(e);
throw e;
} catch(Error e) {
ErrorService.error(e);
throw e;
} catch(Exception e) {
ErrorService.error(e);
throw new UndeclaredThrowableException(e);
}
}
}, null, period != 0);
}
public ListeningScheduledTimerTask(final Callable<V> c, long period) {
task = new ListeningResetableFutureTask<V>(new Callable<V>() {
public V call() {
try {
return c.call();
} catch(RuntimeException e) {
ErrorService.error(e);
throw e;
} catch(Error e) {
ErrorService.error(e);
throw e;
} catch(Exception e) {
ErrorService.error(e);
throw new UndeclaredThrowableException(e);
}
}
}, period != 0);
}
@Override
public void run() {
task.run();
}
public long getDelay(TimeUnit unit) {
return -1;
}
public int compareTo(Delayed o) {
return 0;
}
public boolean cancel(boolean mayInterruptIfRunning) {
cancel();
return task.cancel(mayInterruptIfRunning);
}
public V get() throws InterruptedException, ExecutionException {
return task.get();
}
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,
TimeoutException {
return task.get(timeout, unit);
}
public boolean isCancelled() {
return task.isCancelled();
}
public boolean isDone() {
return task.isDone();
}
@Override
public void addFutureListener(EventListener<FutureEvent<V>> listener) {
task.addFutureListener(listener);
}
}
/** A ListeningRunnableFuture that may run periodically. */
private static class ListeningResetableFutureTask<V> extends ListeningFutureTask<V> {
private final boolean periodic;
public ListeningResetableFutureTask(Runnable runnable, V result, boolean periodic) {
super(runnable, result);
this.periodic = periodic;
}
public ListeningResetableFutureTask(Callable<V> callable, boolean periodic) {
super(callable);
this.periodic = periodic;
}
@Override
public void run() {
if(periodic) {
super.runAndReset();
} else {
super.run();
}
}
}
}