diff --git a/build.third_step8-jdk11.gradle b/build.third_step8-jdk11.gradle
index f320db709..3825698d4 100644
--- a/build.third_step8-jdk11.gradle
+++ b/build.third_step8-jdk11.gradle
@@ -26,6 +26,7 @@ sourceSets{
java{
srcDirs=[
"${srcDir}/fine-ehcache/src/main/java"
+ "${srcDir}/fine-transmittable-thread-local/src/main/java"
]
}
}
@@ -33,7 +34,8 @@ sourceSets{
def resourceDirs = [
"${srcDir}/fine-ehcache/src/main/java",
- "${srcDir}/fine-ehcache/src/main/recources"
+ "${srcDir}/fine-ehcache/src/main/recources",
+ "${srcDir}/fine-transmittable-thread-local/src/main/java"
]
sourceSets.main.java.outputDir = file('build/classes/8')
diff --git a/build.third_step8.gradle b/build.third_step8.gradle
index f2112233b..8410bac6b 100644
--- a/build.third_step8.gradle
+++ b/build.third_step8.gradle
@@ -25,7 +25,8 @@ sourceSets{
main{
java{
srcDirs=[
- "${srcDir}/fine-ehcache/src/main/java"
+ "${srcDir}/fine-ehcache/src/main/java",
+ "${srcDir}/fine-transmittable-thread-local/src/main/java"
]
}
}
@@ -70,6 +71,7 @@ task copyFiles(type:Copy,dependsOn:'compileJava'){
println "------------------------------------------------copyfiles"
with dataContent.call("${srcDir}/fine-ehcache/src/main/java")
with dataContent.call("${srcDir}/fine-ehcache/src/main/recources")
+ with dataContent.call("${srcDir}/fine-transmittable-thread-local/src/main/java")
into "${classesDir}"
}
}
diff --git a/fine-transmittable-thread-local/pom.xml b/fine-transmittable-thread-local/pom.xml
new file mode 100644
index 000000000..4d324253f
--- /dev/null
+++ b/fine-transmittable-thread-local/pom.xml
@@ -0,0 +1,41 @@
+
+ 4.0.0
+
+
+ com.fr.third
+ step8
+ 10.0-FEATURE-SNAPSHOT
+ ../base-third-project/base-third-step8
+
+
+ fine-transmittable-thread-local
+ ${revision}
+
+
+
+
+
+ com.fr.third
+ fine-javassist
+ ${revision}
+
+
+
+ com.fr.third
+ fine-jetbrains
+ ${revision}
+
+
+
+
+
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TransmittableThreadLocal.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TransmittableThreadLocal.java
new file mode 100644
index 000000000..f4d423503
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TransmittableThreadLocal.java
@@ -0,0 +1,754 @@
+package com.fr.third.alibaba.ttl;
+
+import com.fr.third.alibaba.ttl.threadpool.TtlExecutors;
+import com.fr.third.alibaba.ttl.threadpool.TtlForkJoinPoolHelper;
+import com.fr.third.alibaba.ttl.threadpool.agent.TtlAgent;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.Callable;
+import java.util.function.Supplier;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * {@link TransmittableThreadLocal} can transmit value from the thread of submitting task to the thread of executing task.
+ *
+ * Note :
+ * {@link TransmittableThreadLocal} extends {@link InheritableThreadLocal},
+ * so {@link TransmittableThreadLocal} first is a {@link InheritableThreadLocal}.
+ * If the inheritable ability from {@link InheritableThreadLocal} has potential leaking problem ,
+ * you can disable the inheritable ability:
+ *
+ * ❶ by wrapping thread factory using method
+ * {@link TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory)} /
+ * {@link TtlForkJoinPoolHelper#getDefaultDisableInheritableForkJoinWorkerThreadFactory()}
+ * for thread pooling components({@link java.util.concurrent.ThreadPoolExecutor}, {@link java.util.concurrent.ForkJoinPool}).
+ * Inheritable feature in thread pooling components should never happen,
+ * because threads in thread pooling components is pre-created and pooled, these threads is neutral for biz logic/data.
+ *
+ * You can turn on "disable inheritable for thread pool" by {@link TtlAgent}
+ * so as to wrap thread factory for thread pooling components ({@link java.util.concurrent.ThreadPoolExecutor},
+ * {@link java.util.concurrent.ForkJoinPool}) automatically and transparently.
+ *
+ * ❷ or by overriding method {@link #childValue(Object)}.
+ * Whether the value should be inheritable or not can be controlled by the data owner,
+ * disable it carefully when data owner have a clear idea.
+ *
{@code TransmittableThreadLocal transmittableThreadLocal = new TransmittableThreadLocal() {
+ * protected String childValue(String parentValue) {
+ * return initialValue();
+ * }
+ * }}
+ *
+ * More discussion about "disable the inheritable ability"
+ * see
+ * issue #100: disable Inheritable when it's not necessary and buggy .
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @author Yang Fang (snoop dot fy at gmail dot com)
+ * @see TtlRunnable
+ * @see TtlCallable
+ * @see TtlExecutors
+ * @see TtlExecutors#getTtlExecutor(java.util.concurrent.Executor)
+ * @see TtlExecutors#getTtlExecutorService(java.util.concurrent.ExecutorService)
+ * @see TtlExecutors#getTtlScheduledExecutorService(java.util.concurrent.ScheduledExecutorService)
+ * @see TtlExecutors#getDefaultDisableInheritableThreadFactory()
+ * @see TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory)
+ * @see TtlForkJoinPoolHelper
+ * @see TtlForkJoinPoolHelper#getDefaultDisableInheritableForkJoinWorkerThreadFactory()
+ * @see TtlForkJoinPoolHelper#getDisableInheritableForkJoinWorkerThreadFactory(java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory)
+ * @see TtlAgent
+ * @since 0.10.0
+ */
+public class TransmittableThreadLocal extends InheritableThreadLocal implements TtlCopier {
+ private static final Logger logger = Logger.getLogger(TransmittableThreadLocal.class.getName());
+
+ private final boolean disableIgnoreNullValueSemantics;
+
+ /**
+ * Default constructor.
+ *
+ * Create a {@link TransmittableThreadLocal} instance with "Ignore-Null-Value Semantics".
+ * About "Ignore-Null-Value Semantics":
+ *
+ *
+ * If value is {@code null}(check by {@link #get()} method), do NOT transmit this {@code ThreadLocal}.
+ * If set {@code null} value, also remove value(invoke {@link #remove()} method).
+ *
+ *
+ * This is a pragmatic design decision:
+ *
+ * use explicit value type rather than {@code null} to biz intent.
+ * more safe(avoid {@code NPE}) and gc friendly.
+ *
+ *
+ * So it's not recommended to use {@code null} value.
+ *
+ * But the behavior of "Ignore-Null-Value Semantics" is NOT compatible with
+ * {@link ThreadLocal} and {@link InheritableThreadLocal},
+ * you can disable this behavior/semantics via using constructor {@link #TransmittableThreadLocal(boolean)}
+ * and set parameter {@code disableIgnoreNullValueSemantics} instead.
+ *
+ * More info see Issue #157 .
+ *
+ * @see #TransmittableThreadLocal(boolean)
+ */
+ public TransmittableThreadLocal() {
+ this(false);
+ }
+
+ /**
+ * Constructor, create a {@link TransmittableThreadLocal} with parameter {@code disableIgnoreNullValueSemantics}
+ * to control "Ignore-Null-Value Semantics".
+ *
+ * @param disableIgnoreNullValueSemantics disable "Ignore-Null-Value Semantics"
+ * @see #TransmittableThreadLocal()
+ * @since 2.11.3
+ */
+ public TransmittableThreadLocal(boolean disableIgnoreNullValueSemantics) {
+ this.disableIgnoreNullValueSemantics = disableIgnoreNullValueSemantics;
+ }
+
+ /**
+ * Computes the value for this transmittable thread-local variable
+ * as a function of the source thread's value at the time the task
+ * Object is created.
+ *
+ * This method is called from {@link TtlRunnable} or
+ * {@link TtlCallable} when it create, before the task is started.
+ *
+ * This method merely returns reference of its source thread value(the shadow copy),
+ * and should be overridden if a different behavior is desired.
+ *
+ * @since 1.0.0
+ */
+ public T copy(T parentValue) {
+ return parentValue;
+ }
+
+ /**
+ * Callback method before task object({@link TtlRunnable}/{@link TtlCallable}) execute.
+ *
+ * Default behavior is to do nothing, and should be overridden
+ * if a different behavior is desired.
+ *
+ * Do not throw any exception, just ignored.
+ *
+ * @since 1.2.0
+ */
+ protected void beforeExecute() {
+ }
+
+ /**
+ * Callback method after task object({@link TtlRunnable}/{@link TtlCallable}) execute.
+ *
+ * Default behavior is to do nothing, and should be overridden
+ * if a different behavior is desired.
+ *
+ * Do not throw any exception, just ignored.
+ *
+ * @since 1.2.0
+ */
+ protected void afterExecute() {
+ }
+
+ /**
+ * see {@link InheritableThreadLocal#get()}
+ */
+ @Override
+ public final T get() {
+ T value = super.get();
+ if (disableIgnoreNullValueSemantics || null != value) addThisToHolder();
+ return value;
+ }
+
+ /**
+ * see {@link InheritableThreadLocal#set}
+ */
+ @Override
+ public final void set(T value) {
+ if (!disableIgnoreNullValueSemantics && null == value) {
+ // may set null to remove value
+ remove();
+ } else {
+ super.set(value);
+ addThisToHolder();
+ }
+ }
+
+ /**
+ * see {@link InheritableThreadLocal#remove()}
+ */
+ @Override
+ public final void remove() {
+ removeThisFromHolder();
+ super.remove();
+ }
+
+ private void superRemove() {
+ super.remove();
+ }
+
+ private T copyValue() {
+ return copy(get());
+ }
+
+ // Note about the holder:
+ // 1. holder self is a InheritableThreadLocal(a *ThreadLocal*).
+ // 2. The type of value in the holder is WeakHashMap, ?>.
+ // 2.1 but the WeakHashMap is used as a *Set*:
+ // - the value of WeakHashMap is *always null,
+ // - and be never used.
+ // 2.2 WeakHashMap support *null* value.
+ private static InheritableThreadLocal, ?>> holder =
+ new InheritableThreadLocal, ?>>() {
+ @Override
+ protected WeakHashMap, ?> initialValue() {
+ return new WeakHashMap, Object>();
+ }
+
+ @Override
+ protected WeakHashMap, ?> childValue(WeakHashMap, ?> parentValue) {
+ return new WeakHashMap, Object>(parentValue);
+ }
+ };
+
+ @SuppressWarnings("unchecked")
+ private void addThisToHolder() {
+ if (!holder.get().containsKey(this)) {
+ holder.get().put((TransmittableThreadLocal) this, null); // WeakHashMap supports null value.
+ }
+ }
+
+ private void removeThisFromHolder() {
+ holder.get().remove(this);
+ }
+
+ private static void doExecuteCallback(boolean isBefore) {
+ for (TransmittableThreadLocal threadLocal : holder.get().keySet()) {
+ try {
+ if (isBefore) threadLocal.beforeExecute();
+ else threadLocal.afterExecute();
+ } catch (Throwable t) {
+ if (logger.isLoggable(Level.WARNING)) {
+ logger.log(Level.WARNING, "TTL exception when " + (isBefore ? "beforeExecute" : "afterExecute") + ", cause: " + t.toString(), t);
+ }
+ }
+ }
+ }
+
+ /**
+ * Debug only method!
+ */
+ static void dump(@Nullable String title) {
+ if (title != null && title.length() > 0) {
+ System.out.printf("Start TransmittableThreadLocal[%s] Dump...%n", title);
+ } else {
+ System.out.println("Start TransmittableThreadLocal Dump...");
+ }
+
+ for (TransmittableThreadLocal threadLocal : holder.get().keySet()) {
+ System.out.println(threadLocal.get());
+ }
+ System.out.println("TransmittableThreadLocal Dump end!");
+ }
+
+ /**
+ * Debug only method!
+ */
+ static void dump() {
+ dump(null);
+ }
+
+ /**
+ * {@link Transmitter} transmit all {@link TransmittableThreadLocal}
+ * and registered {@link ThreadLocal}(registered by {@link Transmitter#registerThreadLocal})
+ * values of the current thread to other thread by static methods
+ * {@link #capture()} => {@link #replay(Object)} => {@link #restore(Object)} (aka {@code CRR} operation).
+ *
+ * {@link Transmitter} is internal manipulation api for framework/middleware integration ;
+ * In general, you will never use it in the biz/application code !
+ *
+ *
Framework/Middleware integration to TTL transmittance
+ * Below is the example code:
+ *
+ *
+ * ///////////////////////////////////////////////////////////////////////////
+ * // in thread A, capture all TransmittableThreadLocal values of thread A
+ * ///////////////////////////////////////////////////////////////////////////
+ *
+ * Object captured = Transmitter.capture(); // (1)
+ *
+ * ///////////////////////////////////////////////////////////////////////////
+ * // in thread B
+ * ///////////////////////////////////////////////////////////////////////////
+ *
+ * // replay all TransmittableThreadLocal values from thread A
+ * Object backup = Transmitter.replay(captured); // (2)
+ * try {
+ * // your biz logic, run with the TransmittableThreadLocal values of thread B
+ * System.out.println("Hello");
+ * // ...
+ * return "World";
+ * } finally {
+ * // restore the TransmittableThreadLocal of thread B when replay
+ * Transmitter.restore(backup); (3)
+ * }
+ *
+ *
+ * see the implementation code of {@link TtlRunnable} and {@link TtlCallable} for more actual code sample.
+ *
+ * Of course, {@link #replay(Object)} and {@link #restore(Object)} operation can be simplified by util methods
+ * {@link #runCallableWithCaptured(Object, Callable)} or {@link #runSupplierWithCaptured(Object, Supplier)}
+ * and the adorable {@code Java 8 lambda syntax}.
+ *
+ * Below is the example code:
+ *
+ *
+ * ///////////////////////////////////////////////////////////////////////////
+ * // in thread A, capture all TransmittableThreadLocal values of thread A
+ * ///////////////////////////////////////////////////////////////////////////
+ *
+ * Object captured = Transmitter.capture(); // (1)
+ *
+ * ///////////////////////////////////////////////////////////////////////////
+ * // in thread B
+ * ///////////////////////////////////////////////////////////////////////////
+ *
+ * String result = runSupplierWithCaptured(captured, () -> {
+ * // your biz logic, run with the TransmittableThreadLocal values of thread A
+ * System.out.println("Hello");
+ * ...
+ * return "World";
+ * }); // (2) + (3)
+ *
+ *
+ * The reason of providing 2 util methods is the different {@code throws Exception} type
+ * so as to satisfy your biz logic({@code lambda}):
+ *
+ * {@link #runCallableWithCaptured(Object, Callable)}: {@code throws Exception}
+ * {@link #runSupplierWithCaptured(Object, Supplier)}: No {@code throws}
+ *
+ *
+ * If you need the different {@code throws Exception} type,
+ * you can define your own util method(function interface({@code lambda})) with your own {@code throws Exception} type.
+ *
+ *
ThreadLocal Integration
+ * If you can not rewrite the existed code which use {@link ThreadLocal} to {@link TransmittableThreadLocal},
+ * register the {@link ThreadLocal} instances via the methods
+ * {@link #registerThreadLocal(ThreadLocal, TtlCopier)}/{@link #registerThreadLocalWithShadowCopier(ThreadLocal)}
+ * to enhance the Transmittable ability for the existed {@link ThreadLocal} instances.
+ *
+ * Below is the example code:
+ *
+ *
+ * // the value of this ThreadLocal instance will be transmitted after registered
+ * Transmitter.registerThreadLocal(aThreadLocal, copyLambda);
+ *
+ * // Then the value of this ThreadLocal instance will not be transmitted after unregistered
+ * Transmitter.unregisterThreadLocal(aThreadLocal);
+ *
+ *
+ * Caution:
+ * If the registered {@link ThreadLocal} instance is not {@link InheritableThreadLocal},
+ * the instance can NOT {@code inherit} value from parent thread(aka. the inheritable ability)!
+ *
+ * @author Yang Fang (snoop dot fy at gmail dot com)
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @see TtlRunnable
+ * @see TtlCallable
+ * @since 2.3.0
+ */
+ public static class Transmitter {
+ /**
+ * Capture all {@link TransmittableThreadLocal} and registered {@link ThreadLocal} values in the current thread.
+ *
+ * @return the captured {@link TransmittableThreadLocal} values
+ * @since 2.3.0
+ */
+ @NotNull
+ public static Object capture() {
+ return new Snapshot(captureTtlValues(), captureThreadLocalValues());
+ }
+
+ private static HashMap, Object> captureTtlValues() {
+ HashMap, Object> ttl2Value = new HashMap, Object>();
+ for (TransmittableThreadLocal threadLocal : holder.get().keySet()) {
+ ttl2Value.put(threadLocal, threadLocal.copyValue());
+ }
+ return ttl2Value;
+ }
+
+ private static HashMap, Object> captureThreadLocalValues() {
+ final HashMap, Object> threadLocal2Value = new HashMap, Object>();
+ for (Map.Entry, TtlCopier> entry : threadLocalHolder.entrySet()) {
+ final ThreadLocal threadLocal = entry.getKey();
+ final TtlCopier copier = entry.getValue();
+
+ threadLocal2Value.put(threadLocal, copier.copy(threadLocal.get()));
+ }
+ return threadLocal2Value;
+ }
+
+ /**
+ * Replay the captured {@link TransmittableThreadLocal} and registered {@link ThreadLocal} values from {@link #capture()},
+ * and return the backup {@link TransmittableThreadLocal} values in the current thread before replay.
+ *
+ * @param captured captured {@link TransmittableThreadLocal} values from other thread from {@link #capture()}
+ * @return the backup {@link TransmittableThreadLocal} values before replay
+ * @see #capture()
+ * @since 2.3.0
+ */
+ @NotNull
+ public static Object replay(@NotNull Object captured) {
+ final Snapshot capturedSnapshot = (Snapshot) captured;
+ return new Snapshot(replayTtlValues(capturedSnapshot.ttl2Value), replayThreadLocalValues(capturedSnapshot.threadLocal2Value));
+ }
+
+ @NotNull
+ private static HashMap, Object> replayTtlValues(@NotNull HashMap, Object> captured) {
+ HashMap, Object> backup = new HashMap, Object>();
+
+ for (final Iterator> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
+ TransmittableThreadLocal threadLocal = iterator.next();
+
+ // backup
+ backup.put(threadLocal, threadLocal.get());
+
+ // clear the TTL values that is not in captured
+ // avoid the extra TTL values after replay when run task
+ if (!captured.containsKey(threadLocal)) {
+ iterator.remove();
+ threadLocal.superRemove();
+ }
+ }
+
+ // set TTL values to captured
+ setTtlValuesTo(captured);
+
+ // call beforeExecute callback
+ doExecuteCallback(true);
+
+ return backup;
+ }
+
+ private static HashMap, Object> replayThreadLocalValues(@NotNull HashMap, Object> captured) {
+ final HashMap, Object> backup = new HashMap, Object>();
+
+ for (Map.Entry, Object> entry : captured.entrySet()) {
+ final ThreadLocal threadLocal = entry.getKey();
+ backup.put(threadLocal, threadLocal.get());
+
+ final Object value = entry.getValue();
+ if (value == threadLocalClearMark) threadLocal.remove();
+ else threadLocal.set(value);
+ }
+
+ return backup;
+ }
+
+ /**
+ * Clear all {@link TransmittableThreadLocal} and registered {@link ThreadLocal} values in the current thread,
+ * and return the backup {@link TransmittableThreadLocal} values in the current thread before clear.
+ *
+ * @return the backup {@link TransmittableThreadLocal} values before clear
+ * @since 2.9.0
+ */
+ @NotNull
+ public static Object clear() {
+ final HashMap, Object> ttl2Value = new HashMap, Object>();
+
+ final HashMap, Object> threadLocal2Value = new HashMap, Object>();
+ for (Map.Entry, TtlCopier> entry : threadLocalHolder.entrySet()) {
+ final ThreadLocal threadLocal = entry.getKey();
+ threadLocal2Value.put(threadLocal, threadLocalClearMark);
+ }
+
+ return replay(new Snapshot(ttl2Value, threadLocal2Value));
+ }
+
+ /**
+ * Restore the backup {@link TransmittableThreadLocal} and
+ * registered {@link ThreadLocal} values from {@link #replay(Object)}/{@link #clear()}.
+ *
+ * @param backup the backup {@link TransmittableThreadLocal} values from {@link #replay(Object)}/{@link #clear()}
+ * @see #replay(Object)
+ * @see #clear()
+ * @since 2.3.0
+ */
+ public static void restore(@NotNull Object backup) {
+ final Snapshot backupSnapshot = (Snapshot) backup;
+ restoreTtlValues(backupSnapshot.ttl2Value);
+ restoreThreadLocalValues(backupSnapshot.threadLocal2Value);
+ }
+
+ private static void restoreTtlValues(@NotNull HashMap, Object> backup) {
+ // call afterExecute callback
+ doExecuteCallback(false);
+
+ for (final Iterator> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
+ TransmittableThreadLocal threadLocal = iterator.next();
+
+ // clear the TTL values that is not in backup
+ // avoid the extra TTL values after restore
+ if (!backup.containsKey(threadLocal)) {
+ iterator.remove();
+ threadLocal.superRemove();
+ }
+ }
+
+ // restore TTL values
+ setTtlValuesTo(backup);
+ }
+
+ private static void setTtlValuesTo(@NotNull HashMap, Object> ttlValues) {
+ for (Map.Entry, Object> entry : ttlValues.entrySet()) {
+ TransmittableThreadLocal threadLocal = entry.getKey();
+ threadLocal.set(entry.getValue());
+ }
+ }
+
+ private static void restoreThreadLocalValues(@NotNull HashMap, Object> backup) {
+ for (Map.Entry, Object> entry : backup.entrySet()) {
+ final ThreadLocal threadLocal = entry.getKey();
+ threadLocal.set(entry.getValue());
+ }
+ }
+
+ private static class Snapshot {
+ final HashMap, Object> ttl2Value;
+ final HashMap, Object> threadLocal2Value;
+
+ private Snapshot(HashMap, Object> ttl2Value, HashMap, Object> threadLocal2Value) {
+ this.ttl2Value = ttl2Value;
+ this.threadLocal2Value = threadLocal2Value;
+ }
+ }
+
+ /**
+ * Util method for simplifying {@link #replay(Object)} and {@link #restore(Object)} operation.
+ *
+ * @param captured captured {@link TransmittableThreadLocal} values from other thread from {@link #capture()}
+ * @param bizLogic biz logic
+ * @param the return type of biz logic
+ * @return the return value of biz logic
+ * @see #capture()
+ * @see #replay(Object)
+ * @see #restore(Object)
+ * @since 2.3.1
+ */
+ public static R runSupplierWithCaptured(@NotNull Object captured, @NotNull Supplier bizLogic) {
+ Object backup = replay(captured);
+ try {
+ return bizLogic.get();
+ } finally {
+ restore(backup);
+ }
+ }
+
+ /**
+ * Util method for simplifying {@link #clear()} and {@link #restore(Object)} operation.
+ *
+ * @param bizLogic biz logic
+ * @param the return type of biz logic
+ * @return the return value of biz logic
+ * @see #clear()
+ * @see #restore(Object)
+ * @since 2.9.0
+ */
+ public static R runSupplierWithClear(@NotNull Supplier bizLogic) {
+ Object backup = clear();
+ try {
+ return bizLogic.get();
+ } finally {
+ restore(backup);
+ }
+ }
+
+ /**
+ * Util method for simplifying {@link #replay(Object)} and {@link #restore(Object)} operation.
+ *
+ * @param captured captured {@link TransmittableThreadLocal} values from other thread from {@link #capture()}
+ * @param bizLogic biz logic
+ * @param the return type of biz logic
+ * @return the return value of biz logic
+ * @throws Exception exception threw by biz logic
+ * @see #capture()
+ * @see #replay(Object)
+ * @see #restore(Object)
+ * @since 2.3.1
+ */
+ public static R runCallableWithCaptured(@NotNull Object captured, @NotNull Callable bizLogic) throws Exception {
+ Object backup = replay(captured);
+ try {
+ return bizLogic.call();
+ } finally {
+ restore(backup);
+ }
+ }
+
+ /**
+ * Util method for simplifying {@link #clear()} and {@link #restore(Object)} operation.
+ *
+ * @param bizLogic biz logic
+ * @param the return type of biz logic
+ * @return the return value of biz logic
+ * @throws Exception exception threw by biz logic
+ * @see #clear()
+ * @see #restore(Object)
+ * @since 2.9.0
+ */
+ public static R runCallableWithClear(@NotNull Callable bizLogic) throws Exception {
+ Object backup = clear();
+ try {
+ return bizLogic.call();
+ } finally {
+ restore(backup);
+ }
+ }
+
+ private static volatile WeakHashMap, TtlCopier> threadLocalHolder = new WeakHashMap, TtlCopier>();
+ private static final Object threadLocalHolderUpdateLock = new Object();
+ private static final Object threadLocalClearMark = new Object();
+
+ /**
+ * Register the {@link ThreadLocal}(including subclass {@link InheritableThreadLocal}) instances
+ * to enhance the Transmittable ability for the existed {@link ThreadLocal} instances.
+ *
+ * If the registered {@link ThreadLocal} instance is {@link TransmittableThreadLocal} just ignores and return {@code true}.
+ * since a {@link TransmittableThreadLocal} instance itself has the {@code Transmittable} ability,
+ * it is unnecessary to register a {@link TransmittableThreadLocal} instance.
+ *
+ * @param threadLocal the {@link ThreadLocal} instance that to enhance the Transmittable ability
+ * @param copier the {@link TtlCopier}
+ * @return {@code true} if register the {@link ThreadLocal} instance and set {@code copier}, otherwise {@code false}
+ * @see #registerThreadLocal(ThreadLocal, TtlCopier, boolean)
+ * @since 2.11.0
+ */
+ public static boolean registerThreadLocal(@NotNull ThreadLocal threadLocal, @NotNull TtlCopier copier) {
+ return registerThreadLocal(threadLocal, copier, false);
+ }
+
+ /**
+ * Register the {@link ThreadLocal}(including subclass {@link InheritableThreadLocal}) instances
+ * to enhance the Transmittable ability for the existed {@link ThreadLocal} instances.
+ *
+ * Use the shadow copier(transmit the reference directly),
+ * and should use {@link #registerThreadLocal(ThreadLocal, TtlCopier)} to pass a {@link TtlCopier} explicitly
+ * if a different behavior is desired.
+ *
+ * If the registered {@link ThreadLocal} instance is {@link TransmittableThreadLocal} just ignores and return {@code true}.
+ * since a {@link TransmittableThreadLocal} instance itself has the {@code Transmittable} ability,
+ * it is unnecessary to register a {@link TransmittableThreadLocal} instance.
+ *
+ * @param threadLocal the {@link ThreadLocal} instance that to enhance the Transmittable ability
+ * @return {@code true} if register the {@link ThreadLocal} instance and set {@code copier}, otherwise {@code false}
+ * @see #registerThreadLocal(ThreadLocal, TtlCopier)
+ * @see #registerThreadLocal(ThreadLocal, TtlCopier, boolean)
+ * @since 2.11.0
+ */
+ @SuppressWarnings("unchecked")
+ public static boolean registerThreadLocalWithShadowCopier(@NotNull ThreadLocal threadLocal) {
+ return registerThreadLocal(threadLocal, (TtlCopier) shadowCopier, false);
+ }
+
+ /**
+ * Register the {@link ThreadLocal}(including subclass {@link InheritableThreadLocal}) instances
+ * to enhance the Transmittable ability for the existed {@link ThreadLocal} instances.
+ *
+ * If the registered {@link ThreadLocal} instance is {@link TransmittableThreadLocal} just ignores and return {@code true}.
+ * since a {@link TransmittableThreadLocal} instance itself has the {@code Transmittable} ability,
+ * it is unnecessary to register a {@link TransmittableThreadLocal} instance.
+ *
+ * @param threadLocal the {@link ThreadLocal} instance that to enhance the Transmittable ability
+ * @param copier the {@link TtlCopier}
+ * @param force if {@code true}, update {@code copier} to {@link ThreadLocal} instance
+ * when a {@link ThreadLocal} instance is already registered; otherwise, ignore.
+ * @return {@code true} if register the {@link ThreadLocal} instance and set {@code copier}, otherwise {@code false}
+ * @see #registerThreadLocal(ThreadLocal, TtlCopier)
+ * @since 2.11.0
+ */
+ @SuppressWarnings("unchecked")
+ public static boolean registerThreadLocal(@NotNull ThreadLocal threadLocal, @NotNull TtlCopier copier, boolean force) {
+ if (threadLocal instanceof TransmittableThreadLocal) {
+ logger.warning("register a TransmittableThreadLocal instance, this is unnecessary!");
+ return true;
+ }
+
+ synchronized (threadLocalHolderUpdateLock) {
+ if (!force && threadLocalHolder.containsKey(threadLocal)) return false;
+
+ WeakHashMap, TtlCopier> newHolder = new WeakHashMap, TtlCopier>(threadLocalHolder);
+ newHolder.put((ThreadLocal) threadLocal, (TtlCopier) copier);
+ threadLocalHolder = newHolder;
+ return true;
+ }
+ }
+
+ /**
+ * Register the {@link ThreadLocal}(including subclass {@link InheritableThreadLocal}) instances
+ * to enhance the Transmittable ability for the existed {@link ThreadLocal} instances.
+ *
+ * Use the shadow copier(transmit the reference directly),
+ * and should use {@link #registerThreadLocal(ThreadLocal, TtlCopier, boolean)} to pass a {@link TtlCopier} explicitly
+ * if a different behavior is desired.
+ *
+ * If the registered {@link ThreadLocal} instance is {@link TransmittableThreadLocal} just ignores and return {@code true}.
+ * since a {@link TransmittableThreadLocal} instance itself has the {@code Transmittable} ability,
+ * it is unnecessary to register a {@link TransmittableThreadLocal} instance.
+ *
+ * @param threadLocal the {@link ThreadLocal} instance that to enhance the Transmittable ability
+ * @param force if {@code true}, update {@code copier} to {@link ThreadLocal} instance
+ * when a {@link ThreadLocal} instance is already registered; otherwise, ignore.
+ * @return {@code true} if register the {@link ThreadLocal} instance and set {@code copier}, otherwise {@code false}
+ * @see #registerThreadLocal(ThreadLocal, TtlCopier)
+ * @see #registerThreadLocal(ThreadLocal, TtlCopier, boolean)
+ * @since 2.11.0
+ */
+ @SuppressWarnings("unchecked")
+ public static boolean registerThreadLocalWithShadowCopier(@NotNull ThreadLocal threadLocal, boolean force) {
+ return registerThreadLocal(threadLocal, (TtlCopier) shadowCopier, force);
+ }
+
+ /**
+ * Unregister the {@link ThreadLocal} instances
+ * to remove the Transmittable ability for the {@link ThreadLocal} instances.
+ *
+ * If the {@link ThreadLocal} instance is {@link TransmittableThreadLocal} just ignores and return {@code true}.
+ *
+ * @see #registerThreadLocal(ThreadLocal, TtlCopier)
+ * @see #registerThreadLocalWithShadowCopier(ThreadLocal)
+ * @since 2.11.0
+ */
+ public static boolean unregisterThreadLocal(@NotNull ThreadLocal threadLocal) {
+ if (threadLocal instanceof TransmittableThreadLocal) {
+ logger.warning("unregister a TransmittableThreadLocal instance, this is unnecessary!");
+ return true;
+ }
+
+ synchronized (threadLocalHolderUpdateLock) {
+ if (!threadLocalHolder.containsKey(threadLocal)) return false;
+
+ WeakHashMap, TtlCopier> newHolder = new WeakHashMap, TtlCopier>(threadLocalHolder);
+ newHolder.remove(threadLocal);
+ threadLocalHolder = newHolder;
+ return true;
+ }
+ }
+
+ private static final TtlCopier shadowCopier = new TtlCopier() {
+ @Override
+ public Object copy(Object parentValue) {
+ return parentValue;
+ }
+ };
+
+ private Transmitter() {
+ throw new InstantiationError("Must not instantiate this class");
+ }
+ }
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlCallable.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlCallable.java
new file mode 100644
index 000000000..c4c937216
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlCallable.java
@@ -0,0 +1,262 @@
+package com.fr.third.alibaba.ttl;
+
+import com.fr.third.alibaba.ttl.spi.TtlAttachments;
+import com.fr.third.alibaba.ttl.spi.TtlAttachmentsDelegate;
+import com.fr.third.alibaba.ttl.spi.TtlEnhanced;
+import com.fr.third.alibaba.ttl.spi.TtlWrapper;
+import com.fr.third.alibaba.ttl.threadpool.TtlExecutors;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static com.fr.third.alibaba.ttl.TransmittableThreadLocal.Transmitter.*;
+
+/**
+ * {@link TtlCallable} decorate {@link Callable}, so as to get {@link TransmittableThreadLocal}
+ * and transmit it to the time of {@link Callable} execution, needed when use {@link Callable} to thread pool.
+ *
+ * Use factory method {@link #get(Callable)} to get decorated instance.
+ *
+ * Other TTL Wrapper for common {@code Functional Interface} see {@link TtlWrappers}.
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @see TtlExecutors
+ * @see TtlWrappers
+ * @see java.util.concurrent.Executor
+ * @see java.util.concurrent.ExecutorService
+ * @see java.util.concurrent.ThreadPoolExecutor
+ * @see java.util.concurrent.ScheduledThreadPoolExecutor
+ * @see java.util.concurrent.Executors
+ * @see java.util.concurrent.CompletionService
+ * @see java.util.concurrent.ExecutorCompletionService
+ * @since 0.9.0
+ */
+public final class TtlCallable implements Callable, TtlWrapper>, TtlEnhanced, TtlAttachments {
+ private final AtomicReference capturedRef;
+ private final Callable callable;
+ private final boolean releaseTtlValueReferenceAfterCall;
+
+ private TtlCallable(@NotNull Callable callable, boolean releaseTtlValueReferenceAfterCall) {
+ this.capturedRef = new AtomicReference(capture());
+ this.callable = callable;
+ this.releaseTtlValueReferenceAfterCall = releaseTtlValueReferenceAfterCall;
+ }
+
+ /**
+ * wrap method {@link Callable#call()}.
+ */
+ @Override
+ public V call() throws Exception {
+ Object captured = capturedRef.get();
+ if (captured == null || releaseTtlValueReferenceAfterCall && !capturedRef.compareAndSet(captured, null)) {
+ throw new IllegalStateException("TTL value reference is released after call!");
+ }
+
+ Object backup = replay(captured);
+ try {
+ return callable.call();
+ } finally {
+ restore(backup);
+ }
+ }
+
+ /**
+ * return the original/underneath {@link Callable}.
+ */
+ @NotNull
+ public Callable getCallable() {
+ return unwrap();
+ }
+
+ /**
+ * unwrap to the original/underneath {@link Callable}.
+ *
+ * @see TtlUnwrap#unwrap(Object)
+ * @since 2.11.4
+ */
+ @NotNull
+ @Override
+ public Callable unwrap() {
+ return callable;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TtlCallable> that = (TtlCallable>) o;
+
+ return callable.equals(that.callable);
+ }
+
+ @Override
+ public int hashCode() {
+ return callable.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + " - " + callable.toString();
+ }
+
+ /**
+ * Factory method, wrap input {@link Callable} to {@link TtlCallable}.
+ *
+ * This method is idempotent.
+ *
+ * @param callable input {@link Callable}
+ * @return Wrapped {@link Callable}
+ */
+ @Nullable
+ public static TtlCallable get(@Nullable Callable callable) {
+ return get(callable, false);
+ }
+
+
+ /**
+ * Factory method, wrap input {@link Callable} to {@link TtlCallable}.
+ *
+ * This method is idempotent.
+ *
+ * @param callable input {@link Callable}
+ * @param releaseTtlValueReferenceAfterCall release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
+ * @return Wrapped {@link Callable}
+ */
+ @Nullable
+ public static TtlCallable get(@Nullable Callable callable, boolean releaseTtlValueReferenceAfterCall) {
+ return get(callable, releaseTtlValueReferenceAfterCall, false);
+ }
+
+ /**
+ * Factory method, wrap input {@link Callable} to {@link TtlCallable}.
+ *
+ * This method is idempotent.
+ *
+ * @param callable input {@link Callable}
+ * @param releaseTtlValueReferenceAfterCall 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 Callable}
+ */
+ @Nullable
+ public static TtlCallable get(@Nullable Callable callable, boolean releaseTtlValueReferenceAfterCall, boolean idempotent) {
+ if (null == callable) return null;
+
+ if (callable instanceof TtlEnhanced) {
+ // avoid redundant decoration, and ensure idempotency
+ if (idempotent) return (TtlCallable) callable;
+ else throw new IllegalStateException("Already TtlCallable!");
+ }
+ return new TtlCallable(callable, releaseTtlValueReferenceAfterCall);
+ }
+
+ /**
+ * wrap input {@link Callable} Collection to {@link TtlCallable} Collection.
+ *
+ * @param tasks task to be wrapped
+ * @return Wrapped {@link Callable}
+ */
+ @NotNull
+ public static List> gets(@Nullable Collection extends Callable> tasks) {
+ return gets(tasks, false, false);
+ }
+
+ /**
+ * wrap input {@link Callable} Collection to {@link TtlCallable} Collection.
+ *
+ * @param tasks task to be wrapped
+ * @param releaseTtlValueReferenceAfterCall release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
+ * @return Wrapped {@link Callable}
+ */
+ @NotNull
+ public static List> gets(@Nullable Collection extends Callable> tasks, boolean releaseTtlValueReferenceAfterCall) {
+ return gets(tasks, releaseTtlValueReferenceAfterCall, false);
+ }
+
+ /**
+ * wrap input {@link Callable} Collection to {@link TtlCallable} Collection.
+ *
+ * @param tasks task to be wrapped
+ * @param releaseTtlValueReferenceAfterCall 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 Callable}
+ */
+ @NotNull
+ public static List> gets(@Nullable Collection extends Callable> tasks, boolean releaseTtlValueReferenceAfterCall, boolean idempotent) {
+ if (null == tasks) return Collections.emptyList();
+
+ List> copy = new ArrayList>();
+ for (Callable task : tasks) {
+ copy.add(TtlCallable.get(task, releaseTtlValueReferenceAfterCall, idempotent));
+ }
+ return copy;
+ }
+
+ /**
+ * Unwrap {@link TtlCallable} to the original/underneath one.
+ *
+ * this method is {@code null}-safe, when input {@code Callable} parameter is {@code null}, return {@code null};
+ * if input {@code Callable} parameter is not a {@link TtlCallable} just return input {@code Callable}.
+ *
+ * so {@code TtlCallable.unwrap(TtlCallable.get(callable))} will always return the same input {@code callable} object.
+ *
+ * @see #get(Callable)
+ * @since 2.10.2
+ */
+ @Nullable
+ public static Callable unwrap(@Nullable Callable callable) {
+ if (!(callable instanceof TtlCallable)) return callable;
+ else return ((TtlCallable) callable).getCallable();
+ }
+
+ /**
+ * Unwrap {@link TtlCallable} to the original/underneath one.
+ *
+ * Invoke {@link #unwrap(Callable)} for each element in input collection.
+ *
+ * This method is {@code null}-safe, when input {@code Callable} parameter is {@code null}, return a empty list.
+ *
+ * @see #gets(Collection)
+ * @see #unwrap(Callable)
+ * @since 2.10.2
+ */
+ @NotNull
+ public static List> unwraps(@Nullable Collection extends Callable> tasks) {
+ if (null == tasks) return Collections.emptyList();
+
+ List> copy = new ArrayList>();
+ for (Callable task : tasks) {
+ if (!(task instanceof TtlCallable)) copy.add(task);
+ else copy.add(((TtlCallable) task).getCallable());
+ }
+ return copy;
+ }
+
+ private final TtlAttachmentsDelegate ttlAttachment = new TtlAttachmentsDelegate();
+
+ /**
+ * see {@link TtlAttachments#setTtlAttachment(String, Object)}
+ *
+ * @since 2.11.0
+ */
+ @Override
+ public void setTtlAttachment(@NotNull String key, Object value) {
+ ttlAttachment.setTtlAttachment(key, value);
+ }
+
+ /**
+ * see {@link TtlAttachments#getTtlAttachment(String)}
+ *
+ * @since 2.11.0
+ */
+ @Override
+ public T getTtlAttachment(@NotNull String key) {
+ return ttlAttachment.getTtlAttachment(key);
+ }
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlCopier.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlCopier.java
new file mode 100644
index 000000000..6135a2531
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlCopier.java
@@ -0,0 +1,29 @@
+package com.fr.third.alibaba.ttl;
+
+/**
+ * TtlCopier creates the value when {@link TransmittableThreadLocal.Transmitter#capture()},
+ * use the created value when {@link TransmittableThreadLocal.Transmitter#replay(Object)}
+ *
+ * @see TransmittableThreadLocal.Transmitter
+ * @see TransmittableThreadLocal.Transmitter#capture()
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @since 2.11.0
+ */
+@FunctionalInterface
+public interface TtlCopier {
+ /**
+ * Computes the value for {@link TransmittableThreadLocal}
+ * or registered {@link ThreadLocal}(registered by {@link TransmittableThreadLocal.Transmitter#registerThreadLocal})
+ * as a function of the source thread's value at the time the task
+ * Object is created.
+ *
+ * This method is called from {@link TtlRunnable} or
+ * {@link TtlCallable} when it create, before the task is started
+ * (aka. called when {@link TransmittableThreadLocal.Transmitter#capture()}).
+ *
+ * @see TransmittableThreadLocal.Transmitter#registerThreadLocal(ThreadLocal, TtlCopier)
+ * @see TransmittableThreadLocal.Transmitter#registerThreadLocalWithShadowCopier(ThreadLocal)
+ * @see TransmittableThreadLocal.Transmitter#unregisterThreadLocal
+ */
+ T copy(T parentValue);
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlEnhanced.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlEnhanced.java
new file mode 100644
index 000000000..9849dadcf
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlEnhanced.java
@@ -0,0 +1,15 @@
+package com.fr.third.alibaba.ttl;
+
+import com.fr.third.alibaba.ttl.spi.TtlAttachments;
+
+
+/**
+ * @see TtlAttachments
+ * @deprecated Use {@link com.fr.third.alibaba.ttl.spi.TtlEnhanced} instead.
+ */
+@Deprecated
+
+// [ERROR] The class name com.alibaba.ttl.TtlEnhanced shadows
+// the simple name of implemented interface com.alibaba.ttl.spi.TtlEnhanced [com.alibaba.ttl.TtlEnhanced]
+public interface TtlEnhanced extends com.fr.third.alibaba.ttl.spi.TtlEnhanced {
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlRecursiveAction.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlRecursiveAction.java
new file mode 100644
index 000000000..4b0b392a1
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlRecursiveAction.java
@@ -0,0 +1,63 @@
+package com.fr.third.alibaba.ttl;
+
+import com.fr.third.alibaba.ttl.spi.TtlEnhanced;
+import com.fr.third.alibaba.ttl.threadpool.TtlForkJoinPoolHelper;
+import com.fr.third.alibaba.ttl.threadpool.agent.TtlAgent;
+
+import java.util.concurrent.ForkJoinTask;
+
+import static com.fr.third.alibaba.ttl.TransmittableThreadLocal.Transmitter.*;
+
+/**
+ * A recursive resultless {@link ForkJoinTask} enhanced by {@link TransmittableThreadLocal}.
+ *
+ * Recommend to use {@link TtlAgent};
+ * Specially for {@code Java 8} {@link java.util.stream.Stream} and {@link java.util.concurrent.CompletableFuture},
+ * these async task are executed by {@link java.util.concurrent.ForkJoinPool} via {@link ForkJoinTask} at the bottom.
+ *
+ * @author LNAmp
+ * @see java.util.concurrent.RecursiveAction
+ * @see TtlForkJoinPoolHelper
+ * @see TtlAgent
+ * @since 2.4.0
+ */
+public abstract class TtlRecursiveAction extends ForkJoinTask implements TtlEnhanced {
+
+ private static final long serialVersionUID = -5753568484583412377L;
+
+ private final Object captured = capture();
+
+ protected TtlRecursiveAction() {
+ }
+
+ /**
+ * The main computation performed by this task.
+ */
+ protected abstract void compute();
+
+ /**
+ * see {@link ForkJoinTask#getRawResult()}
+ */
+ public final Void getRawResult() {
+ return null;
+ }
+
+ /**
+ * see {@link ForkJoinTask#setRawResult(Object)}
+ */
+ protected final void setRawResult(Void mustBeNull) {
+ }
+
+ /**
+ * Implements execution conventions for RecursiveActions.
+ */
+ protected final boolean exec() {
+ Object backup = replay(captured);
+ try {
+ compute();
+ return true;
+ } finally {
+ restore(backup);
+ }
+ }
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlRecursiveTask.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlRecursiveTask.java
new file mode 100644
index 000000000..6869c9aba
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlRecursiveTask.java
@@ -0,0 +1,72 @@
+package com.fr.third.alibaba.ttl;
+
+import com.fr.third.alibaba.ttl.spi.TtlEnhanced;
+import com.fr.third.alibaba.ttl.threadpool.TtlForkJoinPoolHelper;
+import com.fr.third.alibaba.ttl.threadpool.agent.TtlAgent;
+
+import java.util.concurrent.ForkJoinTask;
+
+import static com.fr.third.alibaba.ttl.TransmittableThreadLocal.Transmitter.*;
+
+/**
+ * A recursive result-bearing {@link ForkJoinTask} enhanced by {@link TransmittableThreadLocal}.
+ *
+ * Recommend to use {@link TtlAgent};
+ * Specially for {@code Java 8} {@link java.util.stream.Stream} and {@link java.util.concurrent.CompletableFuture},
+ * these async task are executed by {@link java.util.concurrent.ForkJoinPool} via {@link ForkJoinTask} at the bottom.
+ *
+ * @author LNAmp
+ * @see java.util.concurrent.RecursiveTask
+ * @see TtlForkJoinPoolHelper
+ * @see TtlAgent
+ * @since 2.4.0
+ */
+public abstract class TtlRecursiveTask extends ForkJoinTask implements TtlEnhanced {
+
+ private static final long serialVersionUID = 1814679366926362436L;
+
+ private final Object captured = capture();
+
+ protected TtlRecursiveTask() {
+ }
+
+ /**
+ * The result of the computation.
+ */
+ V result;
+
+ /**
+ * The main computation performed by this task.
+ *
+ * @return the result of the computation
+ */
+ protected abstract V compute();
+
+ /**
+ * see {@link ForkJoinTask#getRawResult()}
+ */
+ public final V getRawResult() {
+ return result;
+ }
+
+ /**
+ * see {@link ForkJoinTask#setRawResult(Object)}
+ */
+ protected final void setRawResult(V value) {
+ result = value;
+ }
+
+ /**
+ * Implements execution conventions for RecursiveTask.
+ */
+ protected final boolean exec() {
+ Object backup = replay(captured);
+ try {
+ result = compute();
+ return true;
+ } finally {
+ restore(backup);
+ }
+ }
+
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlRunnable.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlRunnable.java
new file mode 100644
index 000000000..e859150dd
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlRunnable.java
@@ -0,0 +1,262 @@
+package com.fr.third.alibaba.ttl;
+
+import com.fr.third.alibaba.ttl.spi.TtlAttachments;
+import com.fr.third.alibaba.ttl.spi.TtlAttachmentsDelegate;
+import com.fr.third.alibaba.ttl.spi.TtlEnhanced;
+import com.fr.third.alibaba.ttl.spi.TtlWrapper;
+import com.fr.third.alibaba.ttl.threadpool.TtlExecutors;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static com.fr.third.alibaba.ttl.TransmittableThreadLocal.Transmitter.*;
+
+/**
+ * {@link TtlRunnable} decorate {@link Runnable}, so as to get {@link TransmittableThreadLocal}
+ * and transmit it to the time of {@link Runnable} execution, needed when use {@link Runnable} to thread pool.
+ *
+ * Use factory methods {@link #get} / {@link #gets} to create instance.
+ *
+ * Other TTL Wrapper for common {@code Functional Interface} see {@link TtlWrappers}.
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @see TtlExecutors
+ * @see TtlWrappers
+ * @see java.util.concurrent.Executor
+ * @see java.util.concurrent.ExecutorService
+ * @see java.util.concurrent.ThreadPoolExecutor
+ * @see java.util.concurrent.ScheduledThreadPoolExecutor
+ * @see java.util.concurrent.Executors
+ * @since 0.9.0
+ */
+public final class TtlRunnable implements Runnable, TtlWrapper, TtlEnhanced, TtlAttachments {
+ private final AtomicReference capturedRef;
+ private final Runnable runnable;
+ private final boolean releaseTtlValueReferenceAfterRun;
+
+ private TtlRunnable(@NotNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
+ this.capturedRef = new AtomicReference(capture());
+ this.runnable = runnable;
+ this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
+ }
+
+ /**
+ * wrap method {@link Runnable#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 {
+ runnable.run();
+ } finally {
+ restore(backup);
+ }
+ }
+
+ /**
+ * return original/unwrapped {@link Runnable}.
+ */
+ @NotNull
+ public Runnable getRunnable() {
+ return unwrap();
+ }
+
+ /**
+ * unwrap to original/unwrapped {@link Runnable}.
+ *
+ * @see TtlUnwrap#unwrap(Object)
+ * @since 2.11.4
+ */
+ @NotNull
+ @Override
+ public Runnable unwrap() {
+ return runnable;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TtlRunnable that = (TtlRunnable) o;
+
+ return runnable.equals(that.runnable);
+ }
+
+ @Override
+ public int hashCode() {
+ return runnable.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + " - " + runnable.toString();
+ }
+
+ /**
+ * Factory method, wrap input {@link Runnable} to {@link TtlRunnable}.
+ *
+ * @param runnable input {@link Runnable}. if input is {@code null}, return {@code null}.
+ * @return Wrapped {@link Runnable}
+ * @throws IllegalStateException when input is {@link TtlRunnable} already.
+ */
+ @Nullable
+ public static TtlRunnable get(@Nullable Runnable runnable) {
+ return get(runnable, false, false);
+ }
+
+ /**
+ * Factory method, wrap input {@link Runnable} to {@link TtlRunnable}.
+ *
+ * @param runnable input {@link Runnable}. if input is {@code null}, return {@code null}.
+ * @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
+ * @return Wrapped {@link Runnable}
+ * @throws IllegalStateException when input is {@link TtlRunnable} already.
+ */
+ @Nullable
+ public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
+ return get(runnable, releaseTtlValueReferenceAfterRun, false);
+ }
+
+ /**
+ * Factory method, wrap input {@link Runnable} to {@link TtlRunnable}.
+ *
+ * @param runnable input {@link Runnable}. if input is {@code null}, return {@code null}.
+ * @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
+ * @param idempotent is idempotent mode or not. if {@code true}, just return input {@link Runnable} when it's {@link TtlRunnable},
+ * otherwise throw {@link IllegalStateException}.
+ * Caution : {@code true} will cover up bugs! DO NOT set, only when you know why.
+ * @return Wrapped {@link Runnable}
+ * @throws IllegalStateException when input is {@link TtlRunnable} already and not idempotent.
+ */
+ @Nullable
+ public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
+ if (null == runnable) return null;
+
+ if (runnable instanceof TtlEnhanced) {
+ // avoid redundant decoration, and ensure idempotency
+ if (idempotent) return (TtlRunnable) runnable;
+ else throw new IllegalStateException("Already TtlRunnable!");
+ }
+ return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun);
+ }
+
+ /**
+ * wrap input {@link Runnable} Collection to {@link TtlRunnable} Collection.
+ *
+ * @param tasks task to be wrapped. if input is {@code null}, return {@code null}.
+ * @return wrapped tasks
+ * @throws IllegalStateException when input is {@link TtlRunnable} already.
+ */
+ @NotNull
+ public static List gets(@Nullable Collection extends Runnable> tasks) {
+ return gets(tasks, false, false);
+ }
+
+ /**
+ * wrap input {@link Runnable} Collection to {@link TtlRunnable} Collection.
+ *
+ * @param tasks task to be wrapped. if input is {@code null}, return {@code null}.
+ * @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
+ * @return wrapped tasks
+ * @throws IllegalStateException when input is {@link TtlRunnable} already.
+ */
+ @NotNull
+ public static List gets(@Nullable Collection extends Runnable> tasks, boolean releaseTtlValueReferenceAfterRun) {
+ return gets(tasks, releaseTtlValueReferenceAfterRun, false);
+ }
+
+ /**
+ * wrap input {@link Runnable} Collection to {@link TtlRunnable} Collection.
+ *
+ * @param tasks task to be wrapped. if input is {@code null}, return {@code null}.
+ * @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
+ * @param idempotent is idempotent mode or not. if {@code true}, just return input {@link Runnable} when it's {@link TtlRunnable},
+ * otherwise throw {@link IllegalStateException}.
+ * Caution : {@code true} will cover up bugs! DO NOT set, only when you know why.
+ * @return wrapped tasks
+ * @throws IllegalStateException when input is {@link TtlRunnable} already and not idempotent.
+ */
+ @NotNull
+ public static List gets(@Nullable Collection extends Runnable> tasks, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
+ if (null == tasks) return Collections.emptyList();
+
+ List copy = new ArrayList();
+ for (Runnable task : tasks) {
+ copy.add(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent));
+ }
+ return copy;
+ }
+
+ /**
+ * Unwrap {@link TtlRunnable} to the original/underneath one.
+ *
+ * this method is {@code null}-safe, when input {@code Runnable} parameter is {@code null}, return {@code null};
+ * if input {@code Runnable} parameter is not a {@link TtlRunnable} just return input {@code Runnable}.
+ *
+ * so {@code TtlRunnable.unwrap(TtlRunnable.get(runnable))} will always return the same input {@code runnable} object.
+ *
+ * @see #get(Runnable)
+ * @since 2.10.2
+ */
+ @Nullable
+ public static Runnable unwrap(@Nullable Runnable runnable) {
+ if (!(runnable instanceof TtlRunnable)) return runnable;
+ else return ((TtlRunnable) runnable).getRunnable();
+ }
+
+ /**
+ * Unwrap {@link TtlRunnable} to the original/underneath one for collection.
+ *
+ * Invoke {@link #unwrap(Runnable)} for each element in input collection.
+ *
+ * This method is {@code null}-safe, when input {@code Runnable} parameter is {@code null}, return a empty list.
+ *
+ * @see #gets(Collection)
+ * @see #unwrap(Runnable)
+ * @since 2.10.2
+ */
+ @NotNull
+ public static List unwraps(@Nullable Collection extends Runnable> tasks) {
+ if (null == tasks) return Collections.emptyList();
+
+ List copy = new ArrayList();
+ for (Runnable task : tasks) {
+ if (!(task instanceof TtlRunnable)) copy.add(task);
+ else copy.add(((TtlRunnable) task).getRunnable());
+ }
+ return copy;
+ }
+
+ private final TtlAttachmentsDelegate ttlAttachment = new TtlAttachmentsDelegate();
+
+ /**
+ * see {@link TtlAttachments#setTtlAttachment(String, Object)}
+ *
+ * @since 2.11.0
+ */
+ @Override
+ public void setTtlAttachment(@NotNull String key, Object value) {
+ ttlAttachment.setTtlAttachment(key, value);
+ }
+
+ /**
+ * see {@link TtlAttachments#getTtlAttachment(String)}
+ *
+ * @since 2.11.0
+ */
+ @Override
+ public T getTtlAttachment(@NotNull String key) {
+ return ttlAttachment.getTtlAttachment(key);
+ }
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlTimerTask.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlTimerTask.java
new file mode 100644
index 000000000..448c8ff12
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlTimerTask.java
@@ -0,0 +1,193 @@
+package com.fr.third.alibaba.ttl;
+
+import com.fr.third.alibaba.ttl.spi.TtlEnhanced;
+import com.fr.third.alibaba.ttl.spi.TtlWrapper;
+import com.fr.third.alibaba.ttl.threadpool.agent.TtlAgent;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static com.fr.third.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 method {@link TimerTask#scheduledExecutionTime()} in
+ * the origin {@link TimerTask} lose effectiveness! Use {@link TtlAgent} instead.
+ *
+ * @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.
+ * @see TtlAgent
+ * @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 TtlWrapper, com.fr.third.alibaba.ttl.spi.TtlEnhanced {
+ private final AtomicReference capturedRef;
+ private final TimerTask timerTask;
+ private final boolean releaseTtlValueReferenceAfterRun;
+
+ private TtlTimerTask(@NotNull 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();
+ }
+
+ /**
+ * return original/unwrapped {@link TimerTask}.
+ */
+ @NotNull
+ public TimerTask getTimerTask() {
+ return unwrap();
+ }
+
+ /**
+ * unwrap to original/unwrapped {@link TimerTask}.
+ *
+ * @see TtlUnwrap#unwrap(Object)
+ * @since 2.11.4
+ */
+ @NotNull
+ @Override
+ public TimerTask unwrap() {
+ 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.equals(that.timerTask);
+ }
+
+ @Override
+ public int hashCode() {
+ return timerTask != null ? timerTask.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + " - " + timerTask.toString();
+ }
+
+ /**
+ * Factory method, wrap input {@link TimerTask} 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 TimerTask} 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 TtlTimerTask} 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 TimerTask} 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 TtlTimerTask} 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);
+ }
+
+ /**
+ * Unwrap {@link TtlTimerTask} to the original/underneath one.
+ *
+ * this method is {@code null}-safe, when input {@code TimerTask} parameter is {@code null}, return {@code null};
+ * if input {@code TimerTask} parameter is not a {@link TtlTimerTask} just return input {@code TimerTask}.
+ *
+ * @see #get(TimerTask)
+ * @since 2.10.2
+ */
+ @Nullable
+ public static TimerTask unwrap(@Nullable TimerTask timerTask) {
+ if (!(timerTask instanceof TtlTimerTask)) return timerTask;
+ else return ((TtlTimerTask) timerTask).getTimerTask();
+ }
+
+ /**
+ * Unwrap {@link TtlTimerTask} to the original/underneath one.
+ *
+ * Invoke {@link #unwrap(TimerTask)} for each element in input collection.
+ *
+ * This method is {@code null}-safe, when input {@code TimerTask} parameter is {@code null}, return a empty list.
+ *
+ * @see #unwrap(TimerTask)
+ * @since 2.10.2
+ */
+ @NotNull
+ public static List unwraps(@Nullable Collection extends TimerTask> tasks) {
+ if (null == tasks) return Collections.emptyList();
+
+ List copy = new ArrayList();
+ for (TimerTask task : tasks) {
+ if (!(task instanceof TtlTimerTask)) copy.add(task);
+ else copy.add(((TtlTimerTask) task).getTimerTask());
+ }
+ return copy;
+ }
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlUnwrap.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlUnwrap.java
new file mode 100644
index 000000000..82a87b63c
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlUnwrap.java
@@ -0,0 +1,70 @@
+package com.fr.third.alibaba.ttl;
+
+import com.fr.third.alibaba.ttl.spi.TtlWrapper;
+import com.fr.third.alibaba.ttl.threadpool.TtlExecutors;
+import com.fr.third.alibaba.ttl.threadpool.TtlForkJoinPoolHelper;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Util methods for TTL Wrapper: unwrap TTL Wrapper and check TTL Wrapper.
+ *
+ * Note:
+ * all methods is {@code null}-safe, when input parameter is {@code null}, return {@code null}.
+ *
+ * Implementation Note:
+ * The util methods in this class should have been inside {@link TtlWrappers}.
+ * But for {@code Java 6} support, it's required splitting the util methods
+ * which involved {@code Java 8} from {@link TtlWrappers}.
+ * In order to avoid loading {@code Java 8} class (eg: {@link java.util.function.Consumer}, {@link java.util.function.Supplier}),
+ * when invoking any methods of {@link TtlWrappers}.
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @see TtlWrappers
+ * @see TtlWrapper
+ * @see TtlRunnable
+ * @see TtlCallable
+ * @since 2.11.4
+ */
+public class TtlUnwrap {
+ /**
+ * Generic unwrap method, unwrap {@code TtlWrapper} to the original/underneath one.
+ *
+ * this method is {@code null}-safe, when input {@code BiFunction} parameter is {@code null}, return {@code null};
+ * if input parameter is not a {@code TtlWrapper} just return input.
+ *
+ * so {@code unwrap} will always return the same input object.
+ *
+ * @see TtlWrappers#wrap(java.util.function.Supplier)
+ * @see TtlWrappers#wrap(java.util.function.Consumer)
+ * @see TtlWrappers#wrap(java.util.function.BiConsumer)
+ * @see TtlWrappers#wrap(java.util.function.Function)
+ * @see TtlWrappers#wrap(java.util.function.BiFunction)
+ * @see TtlRunnable#unwrap(Runnable)
+ * @see TtlCallable#unwrap(Callable)
+ * @see TtlExecutors#unwrap(java.util.concurrent.Executor)
+ * @see TtlExecutors#unwrap(java.util.concurrent.ThreadFactory)
+ * @see TtlForkJoinPoolHelper#unwrap(java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory)
+ * @since 2.11.4
+ */
+ @Nullable
+ @SuppressWarnings("unchecked")
+ public static T unwrap(@Nullable T obj) {
+ if (!isWrapper(obj)) return obj;
+ else return ((TtlWrapper) obj).unwrap();
+ }
+
+ /**
+ * check the input object is a {@code TtlWrapper} or not.
+ *
+ * @since 2.11.4
+ */
+ public static boolean isWrapper(@Nullable T obj) {
+ return obj instanceof TtlWrapper;
+ }
+
+ private TtlUnwrap() {
+ throw new InstantiationError("Must not instantiate this class");
+ }
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlWrappers.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlWrappers.java
new file mode 100644
index 000000000..d932c3568
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlWrappers.java
@@ -0,0 +1,342 @@
+package com.fr.third.alibaba.ttl;
+
+import com.fr.third.alibaba.ttl.spi.TtlEnhanced;
+import com.fr.third.alibaba.ttl.spi.TtlWrapper;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.function.*;
+
+import static com.fr.third.alibaba.ttl.TransmittableThreadLocal.Transmitter.*;
+
+/**
+ * Util methods for TTL Wrapper: wrap common {@code Functional Interface}.
+ *
+ * Note:
+ *
+ * all methods is {@code null}-safe, when input parameter is {@code null}, return {@code null}.
+ * all wrap method skip wrap (aka. just return input parameter), when input parameter is already wrapped.
+ *
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @see TtlRunnable
+ * @see TtlCallable
+ * @see TtlUnwrap
+ * @see TtlWrapper
+ * @since 2.11.4
+ */
+public class TtlWrappers {
+ /**
+ * wrap input {@link Supplier} to TTL wrapper.
+ *
+ * @param supplier input {@link Supplier}
+ * @return Wrapped {@link Supplier}
+ * @see TtlUnwrap#unwrap(Object)
+ * @since 2.11.4
+ */
+ @Nullable
+ public static Supplier wrap(@Nullable Supplier supplier) {
+ if (supplier == null) return null;
+ else if (supplier instanceof TtlEnhanced) return supplier;
+ else return new TtlSupplier(supplier);
+ }
+
+ private static class TtlSupplier implements Supplier, TtlWrapper>, TtlEnhanced {
+ final Supplier supplier;
+ final Object captured;
+
+ TtlSupplier(@NotNull Supplier supplier) {
+ this.supplier = supplier;
+ this.captured = capture();
+ }
+
+ @Override
+ public T get() {
+ final Object backup = replay(captured);
+ try {
+ return supplier.get();
+ } finally {
+ restore(backup);
+ }
+ }
+
+ @NotNull
+ @Override
+ public Supplier unwrap() {
+ return supplier;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TtlSupplier> that = (TtlSupplier>) o;
+
+ return supplier.equals(that.supplier);
+ }
+
+ @Override
+ public int hashCode() {
+ return supplier.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + " - " + supplier.toString();
+ }
+ }
+
+
+ /**
+ * wrap input {@link Consumer} to TTL wrapper.
+ *
+ * @param consumer input {@link Consumer}
+ * @return Wrapped {@link Consumer}
+ * @see TtlUnwrap#unwrap(Object)
+ * @since 2.11.4
+ */
+ @Nullable
+ public static Consumer wrap(@Nullable Consumer consumer) {
+ if (consumer == null) return null;
+ else if (consumer instanceof TtlEnhanced) return consumer;
+ else return new TtlConsumer(consumer);
+ }
+
+ private static class TtlConsumer implements Consumer, TtlWrapper>, TtlEnhanced {
+ final Consumer consumer;
+ final Object captured;
+
+ TtlConsumer(@NotNull Consumer consumer) {
+ this.consumer = consumer;
+ this.captured = capture();
+ }
+
+ @Override
+ public void accept(T t) {
+ final Object backup = replay(captured);
+ try {
+ consumer.accept(t);
+ } finally {
+ restore(backup);
+ }
+ }
+
+ @NotNull
+ @Override
+ public Consumer unwrap() {
+ return consumer;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TtlConsumer> that = (TtlConsumer>) o;
+
+ return consumer.equals(that.consumer);
+ }
+
+ @Override
+ public int hashCode() {
+ return consumer.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + " - " + consumer.toString();
+ }
+ }
+
+
+ /**
+ * wrap input {@link BiConsumer} to TTL wrapper.
+ *
+ * @param consumer input {@link BiConsumer}
+ * @return Wrapped {@link BiConsumer}
+ * @see TtlUnwrap#unwrap(Object)
+ * @since 2.11.4
+ */
+ @Nullable
+ public static BiConsumer wrap(@Nullable BiConsumer consumer) {
+ if (consumer == null) return null;
+ else if (consumer instanceof TtlEnhanced) return consumer;
+ else return new TtlBiConsumer(consumer);
+ }
+
+ private static class TtlBiConsumer implements BiConsumer, TtlWrapper>, TtlEnhanced {
+ final BiConsumer consumer;
+ final Object captured;
+
+ TtlBiConsumer(@NotNull BiConsumer consumer) {
+ this.consumer = consumer;
+ this.captured = capture();
+ }
+
+ @Override
+ public void accept(T t, U u) {
+ final Object backup = replay(captured);
+ try {
+ consumer.accept(t, u);
+ } finally {
+ restore(backup);
+ }
+ }
+
+ @NotNull
+ @Override
+ public BiConsumer unwrap() {
+ return consumer;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TtlBiConsumer, ?> that = (TtlBiConsumer, ?>) o;
+
+ return consumer.equals(that.consumer);
+ }
+
+ @Override
+ public int hashCode() {
+ return consumer.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + " - " + consumer.toString();
+ }
+ }
+
+
+ /**
+ * wrap input {@link Function} to TTL wrapper.
+ *
+ * @param fn input {@link Function}
+ * @return Wrapped {@link Function}
+ * @see TtlUnwrap#unwrap(Object)
+ * @since 2.11.4
+ */
+ @Nullable
+ public static Function wrap(@Nullable Function fn) {
+ if (fn == null) return null;
+ else if (fn instanceof TtlEnhanced) return fn;
+ else return new TtlFunction(fn);
+ }
+
+ private static class TtlFunction implements Function, TtlWrapper>, TtlEnhanced {
+ final Function fn;
+ final Object captured;
+
+ TtlFunction(@NotNull Function fn) {
+ this.fn = fn;
+ this.captured = capture();
+ }
+
+ @Override
+ public R apply(T t) {
+ final Object backup = replay(captured);
+ try {
+ return fn.apply(t);
+ } finally {
+ restore(backup);
+ }
+ }
+
+ @NotNull
+ @Override
+ public Function unwrap() {
+ return fn;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TtlFunction, ?> that = (TtlFunction, ?>) o;
+
+ return fn.equals(that.fn);
+ }
+
+ @Override
+ public int hashCode() {
+ return fn.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + " - " + fn.toString();
+ }
+ }
+
+
+ /**
+ * wrap input {@link BiFunction} to TTL wrapper.
+ *
+ * @param fn input {@link BiFunction}
+ * @return Wrapped {@link BiFunction}
+ * @see TtlUnwrap#unwrap(Object)
+ * @since 2.11.4
+ */
+ @Nullable
+ public static BiFunction wrap(@Nullable BiFunction fn) {
+ if (fn == null) return null;
+ else if (fn instanceof TtlEnhanced) return fn;
+ else return new TtlBiFunction(fn);
+ }
+
+ private static class TtlBiFunction implements BiFunction, TtlWrapper>, TtlEnhanced {
+ final BiFunction fn;
+ final Object captured;
+
+ TtlBiFunction(@NotNull BiFunction fn) {
+ this.fn = fn;
+ this.captured = capture();
+ }
+
+ @Override
+ public R apply(T t, U u) {
+ final Object backup = replay(captured);
+ try {
+ return fn.apply(t, u);
+ } finally {
+ restore(backup);
+ }
+ }
+
+ @NotNull
+ @Override
+ public BiFunction unwrap() {
+ return fn;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TtlBiFunction, ?, ?> that = (TtlBiFunction, ?, ?>) o;
+
+ return fn.equals(that.fn);
+ }
+
+ @Override
+ public int hashCode() {
+ return fn.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + " - " + fn.toString();
+ }
+ }
+
+
+ private TtlWrappers() {
+ throw new InstantiationError("Must not instantiate this class");
+ }
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/package-info.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/package-info.java
new file mode 100644
index 000000000..d842d0a01
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/package-info.java
@@ -0,0 +1,9 @@
+/**
+ * TTL API.
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @see com.fr.third.alibaba.ttl.TransmittableThreadLocal
+ * @see com.fr.third.alibaba.ttl.TtlRunnable
+ * @see com.fr.third.alibaba.ttl.TtlCallable
+ */
+package com.fr.third.alibaba.ttl;
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlAttachments.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlAttachments.java
new file mode 100644
index 000000000..d3f83bff2
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlAttachments.java
@@ -0,0 +1,39 @@
+package com.fr.third.alibaba.ttl.spi;
+
+import com.fr.third.alibaba.ttl.TtlCallable;
+import com.fr.third.alibaba.ttl.TtlRunnable;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * The TTL attachments for TTL tasks, eg: {@link TtlRunnable}, {@link TtlCallable}.
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @since 2.11.0
+ */
+public interface TtlAttachments extends TtlEnhanced {
+ /**
+ * set the TTL attachments for TTL tasks
+ *
+ * @param key attachment key
+ * @param value attachment value
+ * @since 2.11.0
+ */
+ void setTtlAttachment(@NotNull String key, Object value);
+
+ /**
+ * get the TTL attachment for TTL tasks
+ *
+ * @param key attachment key
+ * @since 2.11.0
+ */
+ T getTtlAttachment(@NotNull String key);
+
+ /**
+ * The attachment key of TTL task, weather this task is a auto wrapper task.
+ *
+ * so the value of this attachment is a {@code boolean}.
+ *
+ * @since 2.11.0
+ */
+ String KEY_IS_AUTO_WRAPPER = "ttl.is.auto.wrapper";
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlAttachmentsDelegate.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlAttachmentsDelegate.java
new file mode 100644
index 000000000..fb50ea218
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlAttachmentsDelegate.java
@@ -0,0 +1,31 @@
+package com.fr.third.alibaba.ttl.spi;
+
+import com.fr.third.alibaba.ttl.TtlCallable;
+import com.fr.third.alibaba.ttl.TtlRunnable;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * {@link TtlAttachments} delegate/implementation.
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @see TtlRunnable
+ * @see TtlCallable
+ * @since 2.11.0
+ */
+public class TtlAttachmentsDelegate implements TtlAttachments {
+ private final ConcurrentMap attachments = new ConcurrentHashMap();
+
+ @Override
+ public void setTtlAttachment(@NotNull String key, Object value) {
+ attachments.put(key, value);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T getTtlAttachment(@NotNull String key) {
+ return (T) attachments.get(key);
+ }
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlEnhanced.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlEnhanced.java
new file mode 100644
index 000000000..2fe90b789
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlEnhanced.java
@@ -0,0 +1,20 @@
+package com.fr.third.alibaba.ttl.spi;
+
+import com.fr.third.alibaba.ttl.TtlCallable;
+import com.fr.third.alibaba.ttl.TtlRecursiveAction;
+import com.fr.third.alibaba.ttl.TtlRecursiveTask;
+import com.fr.third.alibaba.ttl.TtlRunnable;
+
+/**
+ * a Ttl marker/tag interface, for ttl enhanced class, for example {@code TTL wrapper} like {@link TtlRunnable}, {@link TtlCallable}.
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @see TtlRunnable
+ * @see TtlCallable
+ * @see TtlRecursiveAction
+ * @see TtlRecursiveTask
+ * @see TtlAttachments
+ * @since 2.11.0
+ */
+public interface TtlEnhanced {
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlWrapper.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlWrapper.java
new file mode 100644
index 000000000..5fd32b1ed
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlWrapper.java
@@ -0,0 +1,26 @@
+package com.fr.third.alibaba.ttl.spi;
+
+import com.fr.third.alibaba.ttl.TtlUnwrap;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Ttl Wrapper interface.
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @see TtlUnwrap#unwrap
+ * @since 2.11.4
+ */
+public interface TtlWrapper extends TtlEnhanced {
+ /**
+ * unwrap {@link TtlWrapper} to the original/underneath one.
+ *
+ * this method is {@code null}-safe, when input {@code BiFunction} parameter is {@code null}, return {@code null};
+ * if input parameter is not a {@code TtlWrapper} just return input.
+ *
+ * so {@code unwrap} will always return the same input object.
+ *
+ * @see TtlUnwrap#unwrap(Object)
+ */
+ @NotNull
+ T unwrap();
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/package-info.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/package-info.java
new file mode 100644
index 000000000..50919ee22
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/package-info.java
@@ -0,0 +1,9 @@
+/**
+ * TTL SPI
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @see com.fr.third.alibaba.ttl.spi.TtlEnhanced
+ * @see com.fr.third.alibaba.ttl.spi.TtlAttachments
+ * @since 2.11.0
+ */
+package com.fr.third.alibaba.ttl.spi;
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableForkJoinWorkerThreadFactory.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableForkJoinWorkerThreadFactory.java
new file mode 100644
index 000000000..44a508b99
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableForkJoinWorkerThreadFactory.java
@@ -0,0 +1,21 @@
+package com.fr.third.alibaba.ttl.threadpool;
+
+import com.fr.third.alibaba.ttl.spi.TtlWrapper;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory;
+
+/**
+ * Disable inheritable {@link ForkJoinWorkerThreadFactory}.
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @since 2.10.1
+ */
+public interface DisableInheritableForkJoinWorkerThreadFactory extends ForkJoinWorkerThreadFactory, TtlWrapper {
+ /**
+ * Unwrap {@link DisableInheritableThreadFactory} to the original/underneath one.
+ */
+ @Override
+ @NotNull
+ ForkJoinWorkerThreadFactory unwrap();
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableForkJoinWorkerThreadFactoryWrapper.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableForkJoinWorkerThreadFactoryWrapper.java
new file mode 100644
index 000000000..d96b8282a
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableForkJoinWorkerThreadFactoryWrapper.java
@@ -0,0 +1,58 @@
+package com.fr.third.alibaba.ttl.threadpool;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory;
+import java.util.concurrent.ForkJoinWorkerThread;
+
+import static com.fr.third.alibaba.ttl.TransmittableThreadLocal.Transmitter.clear;
+import static com.fr.third.alibaba.ttl.TransmittableThreadLocal.Transmitter.restore;
+
+/**
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @since 2.10.1
+ */
+class DisableInheritableForkJoinWorkerThreadFactoryWrapper implements DisableInheritableForkJoinWorkerThreadFactory {
+ private final ForkJoinWorkerThreadFactory threadFactory;
+
+ DisableInheritableForkJoinWorkerThreadFactoryWrapper(@NotNull ForkJoinWorkerThreadFactory threadFactory) {
+ this.threadFactory = threadFactory;
+ }
+
+ @Override
+ public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
+ final Object backup = clear();
+ try {
+ return threadFactory.newThread(pool);
+ } finally {
+ restore(backup);
+ }
+ }
+
+ @Override
+ @NotNull
+ public ForkJoinWorkerThreadFactory unwrap() {
+ return threadFactory;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ DisableInheritableForkJoinWorkerThreadFactoryWrapper that = (DisableInheritableForkJoinWorkerThreadFactoryWrapper) o;
+
+ return threadFactory.equals(that.threadFactory);
+ }
+
+ @Override
+ public int hashCode() {
+ return threadFactory.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + " - " + threadFactory.toString();
+ }
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableThreadFactory.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableThreadFactory.java
new file mode 100644
index 000000000..90049cc3e
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableThreadFactory.java
@@ -0,0 +1,22 @@
+package com.fr.third.alibaba.ttl.threadpool;
+
+import com.fr.third.alibaba.ttl.spi.TtlWrapper;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * Disable inheritable {@link ThreadFactory}.
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @see ThreadFactory
+ * @since 2.10.0
+ */
+public interface DisableInheritableThreadFactory extends ThreadFactory, TtlWrapper {
+ /**
+ * Unwrap {@link DisableInheritableThreadFactory} to the original/underneath one.
+ */
+ @Override
+ @NotNull
+ ThreadFactory unwrap();
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableThreadFactoryWrapper.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableThreadFactoryWrapper.java
new file mode 100644
index 000000000..5ecf93cb0
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableThreadFactoryWrapper.java
@@ -0,0 +1,56 @@
+package com.fr.third.alibaba.ttl.threadpool;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.ThreadFactory;
+
+import static com.fr.third.alibaba.ttl.TransmittableThreadLocal.Transmitter.clear;
+import static com.fr.third.alibaba.ttl.TransmittableThreadLocal.Transmitter.restore;
+
+/**
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @since 2.10.0
+ */
+class DisableInheritableThreadFactoryWrapper implements DisableInheritableThreadFactory {
+ private final ThreadFactory threadFactory;
+
+ DisableInheritableThreadFactoryWrapper(@NotNull ThreadFactory threadFactory) {
+ this.threadFactory = threadFactory;
+ }
+
+ @Override
+ public Thread newThread(@NotNull Runnable r) {
+ final Object backup = clear();
+ try {
+ return threadFactory.newThread(r);
+ } finally {
+ restore(backup);
+ }
+ }
+
+ @NotNull
+ @Override
+ public ThreadFactory unwrap() {
+ return threadFactory;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ DisableInheritableThreadFactoryWrapper that = (DisableInheritableThreadFactoryWrapper) o;
+
+ return threadFactory.equals(that.threadFactory);
+ }
+
+ @Override
+ public int hashCode() {
+ return threadFactory.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + " - " + threadFactory.toString();
+ }
+}
diff --git a/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/ExecutorServiceTtlWrapper.java b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/ExecutorServiceTtlWrapper.java
new file mode 100644
index 000000000..ebb4ea482
--- /dev/null
+++ b/fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/ExecutorServiceTtlWrapper.java
@@ -0,0 +1,101 @@
+package com.fr.third.alibaba.ttl.threadpool;
+
+import com.fr.third.alibaba.ttl.TransmittableThreadLocal;
+import com.fr.third.alibaba.ttl.TtlCallable;
+import com.fr.third.alibaba.ttl.TtlRunnable;
+import com.fr.third.alibaba.ttl.spi.TtlEnhanced;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.*;
+
+/**
+ * {@link TransmittableThreadLocal} Wrapper of {@link ExecutorService},
+ * transmit the {@link TransmittableThreadLocal} from the task submit time of {@link Runnable} or {@link Callable}
+ * to the execution time of {@link Runnable} or {@link Callable}.
+ *
+ * @author Jerry Lee (oldratlee at gmail dot com)
+ * @since 0.9.0
+ */
+class ExecutorServiceTtlWrapper extends ExecutorTtlWrapper implements ExecutorService, TtlEnhanced {
+ private final ExecutorService executorService;
+
+ ExecutorServiceTtlWrapper(@NotNull ExecutorService executorService) {
+ super(executorService);
+ this.executorService = executorService;
+ }
+
+ @Override
+ public void shutdown() {
+ executorService.shutdown();
+ }
+
+ @NotNull
+ @Override
+ public List shutdownNow() {
+ return executorService.shutdownNow();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return executorService.isShutdown();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return executorService.isTerminated();
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException {
+ return executorService.awaitTermination(timeout, unit);
+ }
+
+ @NotNull
+ @Override
+ public Future submit(@NotNull Callable task) {
+ return executorService.submit(TtlCallable.get(task));
+ }
+
+ @NotNull
+ @Override
+ public Future submit(@NotNull Runnable task, T result) {
+ return executorService.submit(TtlRunnable.get(task), result);
+ }
+
+ @NotNull
+ @Override
+ public Future> submit(@NotNull Runnable task) {
+ return executorService.submit(TtlRunnable.get(task));
+ }
+
+ @NotNull
+ @Override
+ public