package com.alibaba.ttl;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicReference;
import static com.alibaba.ttl.TransmittableThreadLocal.Transmitter.*;
/**
* {@link TtlTimerTask} decorate {@link TimerTask}, so as to get {@link TransmittableThreadLocal}
* and transmit it to the time of {@link TtlTimerTask} execution, needed when use {@link TtlTimerTask} to {@link java.util.TimerTask}.
*
* Use factory method {@link #get(TimerTask)} to create instance.
*
* NOTE:
* The {@link TtlTimerTask} make the the method {@link TimerTask#scheduledExecutionTime()} in
* the origin {@link TimerTask} lose effectiveness!
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @see java.util.Timer
* @see TimerTask
* @see Alibaba Java Coding Guidelines - Concurrency - Item 10: [Mandatory] Run multiple TimeTask by using ScheduledExecutorService rather than Timer because Timer will kill all running threads in case of failing to catch exceptions.
* @since 0.9.1
* @deprecated Use {@link TtlRunnable}, {@link java.util.concurrent.ScheduledExecutorService} instead of {@link java.util.Timer}, {@link java.util.TimerTask}.
*/
@Deprecated
public final class TtlTimerTask extends TimerTask implements TtlEnhanced {
private final AtomicReference capturedRef;
private final TimerTask timerTask;
private final boolean releaseTtlValueReferenceAfterRun;
private TtlTimerTask(@Nonnull TimerTask timerTask, boolean releaseTtlValueReferenceAfterRun) {
this.capturedRef = new AtomicReference(capture());
this.timerTask = timerTask;
this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
}
/**
* wrap method {@link TimerTask#run()}.
*/
@Override
public void run() {
Object captured = capturedRef.get();
if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
throw new IllegalStateException("TTL value reference is released after run!");
}
Object backup = replay(captured);
try {
timerTask.run();
} finally {
restore(backup);
}
}
@Override
public boolean cancel() {
timerTask.cancel();
return super.cancel();
}
@Nonnull
public TimerTask getTimerTask() {
return timerTask;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TtlTimerTask that = (TtlTimerTask) o;
return timerTask != null ? timerTask.equals(that.timerTask) : that.timerTask == null;
}
@Override
public int hashCode() {
return timerTask != null ? timerTask.hashCode() : 0;
}
/**
* Factory method, wrap input {@link Runnable} to {@link TtlTimerTask}.
*
* This method is idempotent.
*
* @param timerTask input {@link TimerTask}
* @return Wrapped {@link TimerTask}
*/
@Nullable
public static TtlTimerTask get(@Nullable TimerTask timerTask) {
return get(timerTask, false, false);
}
/**
* Factory method, wrap input {@link Runnable} to {@link TtlTimerTask}.
*
* This method is idempotent.
*
* @param timerTask input {@link TimerTask}
* @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
* @return Wrapped {@link TimerTask}
*/
@Nullable
public static TtlTimerTask get(@Nullable TimerTask timerTask, boolean releaseTtlValueReferenceAfterRun) {
return get(timerTask, releaseTtlValueReferenceAfterRun, false);
}
/**
* Factory method, wrap input {@link Runnable} to {@link TtlTimerTask}.
*
* This method is idempotent.
*
* @param timerTask input {@link TimerTask}
* @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
* @param idempotent is idempotent or not. {@code true} will cover up bugs! DO NOT set, only when you know why.
* @return Wrapped {@link TimerTask}
*/
@Nullable
public static TtlTimerTask get(@Nullable TimerTask timerTask, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
if (null == timerTask) return null;
if (timerTask instanceof TtlEnhanced) {
// avoid redundant decoration, and ensure idempotency
if (idempotent) return (TtlTimerTask) timerTask;
else throw new IllegalStateException("Already TtlTimerTask!");
}
return new TtlTimerTask(timerTask, releaseTtlValueReferenceAfterRun);
}
}