ttl2-compatible/src/main/java/com/alibaba/ttl/TransmittableThreadLocal.java
package com.alibaba.ttl;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.jetbrains.annotations.TestOnly;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* {@link TransmittableThreadLocal}({@code TTL}) can transmit the value from the thread of submitting task
* to the thread of executing task even using thread pooling components.
* <p>
* <b>Note</b>:<br>
* {@link TransmittableThreadLocal} extends {@link InheritableThreadLocal},
* so {@link TransmittableThreadLocal} first is a {@link InheritableThreadLocal}.<br>
* If the <b>inheritable</b> ability from {@link InheritableThreadLocal} has <b>potential leaking problem</b>,
* you can disable the <b>inheritable</b> ability:
* <p>
* ❶ For thread pooling components({@link java.util.concurrent.ThreadPoolExecutor},
* {@link java.util.concurrent.ForkJoinPool}), Inheritable feature <b>should never</b> happen,
* since threads in thread pooling components is pre-created and pooled, these threads is <b>neutral</b> to biz logic/data.
* <br>
* Disable inheritable for thread pooling components by wrapping thread factories using methods
* {@link com.alibaba.ttl.threadpool.TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory) getDisableInheritableThreadFactory} /
* {@link com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper#getDefaultDisableInheritableForkJoinWorkerThreadFactory() getDefaultDisableInheritableForkJoinWorkerThreadFactory}.
* <br>
* Or you can turn on "disable inheritable for thread pool" by {@link com.alibaba.ttl.threadpool.agent.TtlAgent}
* to wrap thread factories for thread pooling components automatically and transparently.
* <p>
* ❷ In other cases, disable inheritable by overriding method {@link #childValue(Object)}.
* <br>
* Whether the value should be inheritable or not can be controlled by the data owner,
* disable it <b>carefully</b> when data owner have a clear idea.
* <pre>{@code
* TransmittableThreadLocal<String> transmittableThreadLocal = new TransmittableThreadLocal<>() {
* protected String childValue(String parentValue) {
* return initialValue();
* }
* }}</pre>
* <p>
* More discussion about "disable the <b>inheritable</b> ability"
* see <a href="https://github.com/alibaba/transmittable-thread-local/issues/100">
* issue #100: disable Inheritable when it's not necessary and buggy</a>.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @author Yang Fang (snoop dot fy at gmail dot com)
* @see <a href="https://github.com/alibaba/transmittable-thread-local">user guide docs and code repo of TransmittableThreadLocal(TTL)</a>
* @see TtlRunnable
* @see TtlCallable
* @see com.alibaba.ttl.threadpool.TtlExecutors
* @see com.alibaba.ttl.threadpool.TtlExecutors#getTtlExecutor(java.util.concurrent.Executor)
* @see com.alibaba.ttl.threadpool.TtlExecutors#getTtlExecutorService(java.util.concurrent.ExecutorService)
* @see com.alibaba.ttl.threadpool.TtlExecutors#getTtlScheduledExecutorService(java.util.concurrent.ScheduledExecutorService)
* @see com.alibaba.ttl.threadpool.TtlExecutors#getDefaultDisableInheritableThreadFactory()
* @see com.alibaba.ttl.threadpool.TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory)
* @see com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper
* @see com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper#getDefaultDisableInheritableForkJoinWorkerThreadFactory()
* @see com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper#getDisableInheritableForkJoinWorkerThreadFactory(java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory)
* @see com.alibaba.ttl.threadpool.agent.TtlAgent
* @since 0.10.0
*/
public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> implements TtlCopier<T> {
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".
* <p>
* About "Ignore-Null-Value Semantics":
*
* <ol>
* <li>If value is {@code null}(check by {@link #get()} method), do NOT transmit this {@code ThreadLocal}.</li>
* <li>If set {@code null} value, also remove value(invoke {@link #remove()} method).</li>
* </ol>
* <p>
* This is a pragmatic design decision:
* <ol>
* <li>use explicit value type rather than {@code null} value to express biz intent.</li>
* <li>safer and more robust code(avoid {@code NPE} risk).</li>
* </ol>
* <p>
* So it's strongly not recommended to use {@code null} value.
* <p>
* 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 setting parameter {@code disableIgnoreNullValueSemantics} to {@code true}.
* <p>
* More discussion about "Ignore-Null-Value Semantics" see
* <a href="https://github.com/alibaba/transmittable-thread-local/issues/157">Issue #157</a>.
*
* @see #TransmittableThreadLocal(boolean)
*/
public TransmittableThreadLocal() {
this(false);
}
/**
* Constructor, create a {@link TransmittableThreadLocal} instance
* 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;
}
/**
* Creates a transmittable thread local variable.
* The initial value({@link #initialValue()}) of the variable is
* determined by invoking the {@link #get()} method on the {@code Supplier}.
*
* @param <S> the type of the thread local's value
* @param supplier the supplier to be used to determine the initial value
* @return a new transmittable thread local variable
* @throws NullPointerException if the specified supplier is null
* @see #withInitialAndCopier(Supplier, TtlCopier)
* @since 2.12.2
*/
@NonNull
@SuppressWarnings("ConstantConditions")
public static <S> TransmittableThreadLocal<S> withInitial(@NonNull Supplier<? extends S> supplier) {
if (supplier == null) throw new NullPointerException("supplier is null");
return new SuppliedTransmittableThreadLocal<>(supplier, null, null);
}
/**
* Creates a transmittable thread local variable.
* The initial value({@link #initialValue()}) of the variable is
* determined by invoking the {@link #get()} method on the {@code Supplier};
* and the child value({@link #childValue(Object)}) and the transmitting value({@link #copy(Object)}) of the variable is
* determined by invoking the {@link TtlCopier#copy(Object)} method on the {@code TtlCopier}.
*
* @param <S> the type of the thread local's value
* @param supplier the supplier to be used to determine the initial value
* @param copierForChildValueAndCopy the ttl copier to be used to determine the child value and the transmitting value
* @return a new transmittable thread local variable
* @throws NullPointerException if the specified supplier or copier is null
* @see #withInitial(Supplier)
* @since 2.12.3
*/
@NonNull
@ParametersAreNonnullByDefault
@SuppressWarnings("ConstantConditions")
public static <S> TransmittableThreadLocal<S> withInitialAndCopier(Supplier<? extends S> supplier, TtlCopier<S> copierForChildValueAndCopy) {
if (supplier == null) throw new NullPointerException("supplier is null");
if (copierForChildValueAndCopy == null) throw new NullPointerException("ttl copier is null");
return new SuppliedTransmittableThreadLocal<>(supplier, copierForChildValueAndCopy, copierForChildValueAndCopy);
}
/**
* Creates a transmittable thread local variable.
* The initial value({@link #initialValue()}) of the variable is
* determined by invoking the {@link #get()} method on the {@code Supplier};
* and the child value({@link #childValue(Object)}) and the transmitting value({@link #copy(Object)}) of the variable is
* determined by invoking the {@link TtlCopier#copy(Object)} method on the {@code TtlCopier}.
* <p>
* <B><I>NOTE:</I></B><br>
* Recommend use {@link #withInitialAndCopier(Supplier, TtlCopier)} instead of this method.
* In most cases, the logic of determining the child value({@link #childValue(Object)})
* and the transmitting value({@link #copy(Object)}) should be the same.
*
* @param <S> the type of the thread local's value
* @param supplier the supplier to be used to determine the initial value
* @param copierForChildValue the ttl copier to be used to determine the child value
* @param copierForCopy the ttl copier to be used to determine the transmitting value
* @return a new transmittable thread local variable
* @throws NullPointerException if the specified supplier or copier is null
* @see #withInitial(Supplier)
* @see #withInitialAndCopier(Supplier, TtlCopier)
* @since 2.12.3
*/
@NonNull
@ParametersAreNonnullByDefault
@SuppressWarnings("ConstantConditions")
public static <S> TransmittableThreadLocal<S> withInitialAndCopier(Supplier<? extends S> supplier, TtlCopier<S> copierForChildValue, TtlCopier<S> copierForCopy) {
if (supplier == null) throw new NullPointerException("supplier is null");
if (copierForChildValue == null) throw new NullPointerException("ttl copier for child value is null");
if (copierForCopy == null) throw new NullPointerException("ttl copier for copy value is null");
return new SuppliedTransmittableThreadLocal<>(supplier, copierForChildValue, copierForCopy);
}
/**
* An extension of ThreadLocal that obtains its initial value from the specified {@code Supplier}
* and obtains its child value and transmitting value from the specified ttl copier.
*/
private static final class SuppliedTransmittableThreadLocal<T> extends TransmittableThreadLocal<T> {
private final Supplier<? extends T> supplier;
private final TtlCopier<T> copierForChildValue;
private final TtlCopier<T> copierForCopy;
SuppliedTransmittableThreadLocal(Supplier<? extends T> supplier, TtlCopier<T> copierForChildValue, TtlCopier<T> copierForCopy) {
if (supplier == null) throw new NullPointerException("supplier is null");
this.supplier = supplier;
this.copierForChildValue = copierForChildValue;
this.copierForCopy = copierForCopy;
}
@Override
protected T initialValue() {
return supplier.get();
}
@Override
protected T childValue(T parentValue) {
if (copierForChildValue != null) return copierForChildValue.copy(parentValue);
else return super.childValue(parentValue);
}
@Override
public T copy(T parentValue) {
if (copierForCopy != null) return copierForCopy.copy(parentValue);
else return super.copy(parentValue);
}
}
/**
* 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.
* <p>
* This method is called from {@link TtlRunnable} or
* {@link TtlCallable} when it create, before the task is started.
* <p>
* 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.
* <p>
* Default behavior is to do nothing, and should be overridden
* if a different behavior is desired.
* <p>
* Do not throw any exception, just ignored.
*
* @since 1.2.0
*/
protected void beforeExecute() {
}
/**
* Callback method after task object({@link TtlRunnable}/{@link TtlCallable}) execute.
* <p>
* Default behavior is to do nothing, and should be overridden
* if a different behavior is desired.
* <p>
* Do not throw any exception, just ignored.
*
* @since 1.2.0
*/
protected void afterExecute() {
}
/**
* {@inheritDoc}
*/
@Override
public final T get() {
T value = super.get();
if (disableIgnoreNullValueSemantics || value != null) addThisToHolder();
return value;
}
/**
* {@inheritDoc}
*/
@Override
public final void set(T value) {
if (!disableIgnoreNullValueSemantics && value == null) {
// may set null to remove value
remove();
} else {
super.set(value);
addThisToHolder();
}
}
/**
* {@inheritDoc}
*/
@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<TransmittableThreadLocal<Object>, ?>.
// 2.1 but the WeakHashMap is used as a *Set*:
// the value of WeakHashMap is *always* null, and never used.
// 2.2 WeakHashMap support *null* value.
private static final InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>> holder =
new InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>>() {
@Override
protected WeakHashMap<TransmittableThreadLocal<Object>, ?> initialValue() {
return new WeakHashMap<>();
}
@Override
protected WeakHashMap<TransmittableThreadLocal<Object>, ?> childValue(WeakHashMap<TransmittableThreadLocal<Object>, ?> parentValue) {
return new WeakHashMap<>(parentValue);
}
};
@SuppressWarnings("unchecked")
private void addThisToHolder() {
if (!holder.get().containsKey(this)) {
holder.get().put((TransmittableThreadLocal<Object>) this, null); // WeakHashMap supports null value.
}
}
private void removeThisFromHolder() {
holder.get().remove(this);
}
private static void doExecuteCallback(boolean isBefore) {
// copy TTL Instances to avoid `ConcurrentModificationException`
// even adjust TTL instances in biz lifecycle callbacks(beforeExecute/afterExecute)
WeakHashMap<TransmittableThreadLocal<Object>, ?> ttlInstances = new WeakHashMap<TransmittableThreadLocal<Object>, Object>(holder.get());
for (TransmittableThreadLocal<Object> threadLocal : ttlInstances.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, t);
}
}
}
}
/**
* Debug only method!
*/
@TestOnly
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<Object> threadLocal : holder.get().keySet()) {
System.out.println(threadLocal.get());
}
System.out.println("TransmittableThreadLocal Dump end!");
}
/**
* Debug only method!
*/
@TestOnly
static void dump() {
dump(null);
}
/**
* {@link Transmitter Transmitter} transmit all {@link TransmittableThreadLocal}
* and registered {@link ThreadLocal} values of the current thread to other thread.
* <p>
* Transmittance is completed by static methods {@link #capture()} =>
* {@link #replay(Object)} => {@link #restore(Object)} (aka {@code CRR} operations);
* {@link ThreadLocal} instances are registered by method {@link Transmitter#registerThreadLocal Transmitter#registerThreadLocal}.
* <p>
* {@link Transmitter Transmitter} is <b><i>internal</i></b> manipulation api for <b><i>framework/middleware integration</i></b>;
* In general, you will <b><i>never</i></b> use it in the <i>biz/application codes</i>!
*
* <h2>Framework/Middleware integration to TTL transmittance</h2>
* Below is the example code:
*
* <pre>{@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)
* }}</pre>
* <p>
* see the implementation code of {@link TtlRunnable} and {@link TtlCallable} for more actual code samples.
* <p>
* Of course, {@link #replay(Object)} and {@link #restore(Object)} operations can be simplified by util methods
* {@link #runCallableWithCaptured(Object, Callable)} or {@link #runSupplierWithCaptured(Object, Supplier)}
* and the adorable {@code Java 8 lambda syntax}.
* <p>
* Below is the example code:
*
* <pre>{@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)}</pre>
* <p>
* The reason of providing 2 util methods is the different {@code throws Exception} type
* to satisfy your biz logic({@code lambda}):
* <ol>
* <li>{@link #runCallableWithCaptured(Object, Callable)}: {@code throws Exception}</li>
* <li>{@link #runSupplierWithCaptured(Object, Supplier)}: No {@code throws}</li>
* </ol>
* <p>
* 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.
*
* <h2>ThreadLocal Integration</h2>
* 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 <b>Transmittable</b> ability for the existed {@link ThreadLocal} instances.
* <p>
* Below is the example code:
*
* <pre>{@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);}</pre>
* <p>
* The fields stored the {@code ThreadLocal} instances are generally {@code private static},
* so the {@code ThreadLocal} instances need be got by reflection, for example:
*
* <pre>
* Field field = TheClassStoredThreadLocal.class.getDeclaredField(staticFieldName);
* field.setAccessible(true);
* {@code @SuppressWarnings("unchecked")}
* {@code ThreadLocal<T>} threadLocal = {@code (ThreadLocal<T>)} field.get(null);</pre>
*
* <B><I>Caution:</I></B><br>
* If the registered {@link ThreadLocal} instance is not {@link InheritableThreadLocal},
* the instance can NOT <B><I>{@code inherit}</I></B> value from parent thread(aka. the <b>inheritable</b> 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
*/
@NonNull
public static Object capture() {
final HashMap<Transmittee<Object, Object>, Object> transmittee2Value = new HashMap<>(transmitteeSet.size());
for (Transmittee<Object, Object> transmittee : transmitteeSet) {
try {
transmittee2Value.put(transmittee, transmittee.capture());
} catch (Throwable t) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "exception when Transmitter.capture for transmittee " + transmittee +
"(class " + transmittee.getClass().getName() + "), just ignored; cause: " + t, t);
}
}
}
return new Snapshot(transmittee2Value);
}
/**
* 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
*/
@NonNull
public static Object replay(@NonNull Object captured) {
final Snapshot capturedSnapshot = (Snapshot) captured;
final HashMap<Transmittee<Object, Object>, Object> transmittee2Value = new HashMap<>(capturedSnapshot.transmittee2Value.size());
for (Map.Entry<Transmittee<Object, Object>, Object> entry : capturedSnapshot.transmittee2Value.entrySet()) {
Transmittee<Object, Object> transmittee = entry.getKey();
try {
Object transmitteeCaptured = entry.getValue();
transmittee2Value.put(transmittee, transmittee.replay(transmitteeCaptured));
} catch (Throwable t) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "exception when Transmitter.replay for transmittee " + transmittee +
"(class " + transmittee.getClass().getName() + "), just ignored; cause: " + t, t);
}
}
}
return new Snapshot(transmittee2Value);
}
/**
* 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.
* <p>
* Semantically, the code {@code `Object backup = clear();`} is same as {@code `Object backup = replay(EMPTY_CAPTURE);`}.
* <p>
* The reason for providing this method is:
*
* <ol>
* <li>lead to more readable code</li>
* <li>need not provide the constant {@code EMPTY_CAPTURE}.</li>
* </ol>
*
* @return the backup {@link TransmittableThreadLocal} values before clear
* @see #replay(Object)
* @since 2.9.0
*/
@NonNull
public static Object clear() {
final HashMap<Transmittee<Object, Object>, Object> transmittee2Value = new HashMap<>(transmitteeSet.size());
for (Transmittee<Object, Object> transmittee : transmitteeSet) {
try {
transmittee2Value.put(transmittee, transmittee.clear());
} catch (Throwable t) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "exception when Transmitter.clear for transmittee " + transmittee +
"(class " + transmittee.getClass().getName() + "), just ignored; cause: " + t, t);
}
}
}
return new Snapshot(transmittee2Value);
}
/**
* 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(@NonNull Object backup) {
for (Map.Entry<Transmittee<Object, Object>, Object> entry : ((Snapshot) backup).transmittee2Value.entrySet()) {
Transmittee<Object, Object> transmittee = entry.getKey();
try {
Object transmitteeBackup = entry.getValue();
transmittee.restore(transmitteeBackup);
} catch (Throwable t) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "exception when Transmitter.restore for transmittee " + transmittee +
"(class " + transmittee.getClass().getName() + "), just ignored; cause: " + t, t);
}
}
}
}
private static class Snapshot {
final HashMap<Transmittee<Object, Object>, Object> transmittee2Value;
public Snapshot(HashMap<Transmittee<Object, Object>, Object> transmittee2Value) {
this.transmittee2Value = transmittee2Value;
}
}
/**
* Register the transmittee({@code CRR}), the extension point for other {@code ThreadLocal}.
*
* @param <C> the transmittee capture data type
* @param <B> the transmittee backup data type
* @return true if the input transmittee is not registered
* @see #unregisterTransmittee(Transmittee)
* @since 2.14.0
*/
@SuppressWarnings("unchecked")
public static <C, B> boolean registerTransmittee(@NonNull Transmittee<C, B> transmittee) {
return transmitteeSet.add((Transmittee<Object, Object>) transmittee);
}
/**
* Unregister the transmittee({@code CRR}), the extension point for other {@code ThreadLocal}.
*
* @param <C> the transmittee capture data type
* @param <B> the transmittee backup data type
* @return true if the input transmittee is registered
* @see #registerTransmittee(Transmittee)
* @since 2.14.0
*/
@SuppressWarnings("unchecked")
public static <C, B> boolean unregisterTransmittee(@NonNull Transmittee<C, B> transmittee) {
return transmitteeSet.remove((Transmittee<Object, Object>) transmittee);
}
/**
* The transmittee is the extension point for other {@code ThreadLocal}s
* which are registered by {@link #registerTransmittee(Transmittee) registerTransmittee} method.
* Transmittance is completed by methods {@link #capture() capture()} =>
* {@link #replay(Object) replay(Object)} => {@link #restore(Object) restore(Object)} (aka {@code CRR} operations),
*
* @param <C> the transmittee capture data type
* @param <B> the transmittee backup data type
* @see #registerTransmittee(Transmittee)
* @see #unregisterTransmittee(Transmittee)
* @since 2.14.0
*/
public interface Transmittee<C, B> {
/**
* Capture.
* <p>
* <B><I>NOTE:</I></B>
* <ul>
* <li>do NOT return {@code null}.</li>
* <li>do NOT throw any exceptions, just ignored.</li>
* </ul>
*
* @return the capture data of transmittee
* @since 2.14.0
*/
@NonNull
C capture();
/**
* Replay.
* <p>
* <B><I>NOTE:</I></B>
* <ul>
* <li>do NOT return {@code null}.</li>
* <li>do NOT throw any exceptions, just ignored.</li>
* </ul>
*
* @param captured the capture data of transmittee, the return value of method {@link #capture()}
* @return the backup data of transmittee
* @since 2.14.0
*/
@NonNull
B replay(@NonNull C captured);
/**
* Clear.
* <p>
* <B><I>NOTE:</I></B>
* <ul>
* <li>do NOT return {@code null}.</li>
* <li>do NOT throw any exceptions, just ignored.</li>
* </ul>
* <p>
* Semantically, the code {@code `B backup = clear();`} is same as {@code `B backup = replay(EMPTY_CAPTURE);`}.
* <p>
* The reason for providing this method is:
* <ol>
* <li>lead to more readable code</li>
* <li>need not provide the constant {@code EMPTY_CAPTURE}.</li>
* </ol>
*
* @return the backup data of transmittee
* @since 2.14.0
*/
@NonNull
B clear();
/**
* Restore.
* <p>
* <B><I>NOTE:</I></B><br>
* do NOT throw any exceptions, just ignored.
*
* @param backup the backup data of transmittee, the return value of methods {@link #replay(Object)} or {@link #clear()}
* @see #replay(Object)
* @see #clear()
* @since 2.14.0
*/
void restore(@NonNull B backup);
}
private static final Transmittee<HashMap<TransmittableThreadLocal<Object>, Object>, HashMap<TransmittableThreadLocal<Object>, Object>> ttlTransmittee =
new Transmittee<HashMap<TransmittableThreadLocal<Object>, Object>, HashMap<TransmittableThreadLocal<Object>, Object>>() {
@NonNull
@Override
public HashMap<TransmittableThreadLocal<Object>, Object> capture() {
final HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value = new HashMap<>(holder.get().size());
for (TransmittableThreadLocal<Object> threadLocal : holder.get().keySet()) {
ttl2Value.put(threadLocal, threadLocal.copyValue());
}
return ttl2Value;
}
@NonNull
@Override
public HashMap<TransmittableThreadLocal<Object>, Object> replay(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> captured) {
final HashMap<TransmittableThreadLocal<Object>, Object> backup = new HashMap<>(holder.get().size());
for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
TransmittableThreadLocal<Object> 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;
}
@NonNull
@Override
public HashMap<TransmittableThreadLocal<Object>, Object> clear() {
return replay(new HashMap<>(0));
}
@Override
public void restore(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> backup) {
// call afterExecute callback
doExecuteCallback(false);
for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
TransmittableThreadLocal<Object> 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(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> ttlValues) {
for (Map.Entry<TransmittableThreadLocal<Object>, Object> entry : ttlValues.entrySet()) {
TransmittableThreadLocal<Object> threadLocal = entry.getKey();
threadLocal.set(entry.getValue());
}
}
private static final Transmittee<HashMap<ThreadLocal<Object>, Object>, HashMap<ThreadLocal<Object>, Object>> threadLocalTransmittee =
new Transmittee<HashMap<ThreadLocal<Object>, Object>, HashMap<ThreadLocal<Object>, Object>>() {
@NonNull
@Override
public HashMap<ThreadLocal<Object>, Object> capture() {
final HashMap<ThreadLocal<Object>, Object> threadLocal2Value = new HashMap<>(threadLocalHolder.size());
for (Map.Entry<ThreadLocal<Object>, TtlCopier<Object>> entry : threadLocalHolder.entrySet()) {
final ThreadLocal<Object> threadLocal = entry.getKey();
final TtlCopier<Object> copier = entry.getValue();
threadLocal2Value.put(threadLocal, copier.copy(threadLocal.get()));
}
return threadLocal2Value;
}
@NonNull
@Override
public HashMap<ThreadLocal<Object>, Object> replay(@NonNull HashMap<ThreadLocal<Object>, Object> captured) {
final HashMap<ThreadLocal<Object>, Object> backup = new HashMap<>(captured.size());
for (Map.Entry<ThreadLocal<Object>, Object> entry : captured.entrySet()) {
final ThreadLocal<Object> threadLocal = entry.getKey();
backup.put(threadLocal, threadLocal.get());
final Object value = entry.getValue();
if (value == threadLocalClearMark) threadLocal.remove();
else threadLocal.set(value);
}
return backup;
}
@NonNull
@Override
public HashMap<ThreadLocal<Object>, Object> clear() {
final HashMap<ThreadLocal<Object>, Object> threadLocal2Value = new HashMap<>(threadLocalHolder.size());
for (Map.Entry<ThreadLocal<Object>, TtlCopier<Object>> entry : threadLocalHolder.entrySet()) {
final ThreadLocal<Object> threadLocal = entry.getKey();
threadLocal2Value.put(threadLocal, threadLocalClearMark);
}
return replay(threadLocal2Value);
}
@Override
public void restore(@NonNull HashMap<ThreadLocal<Object>, Object> backup) {
for (Map.Entry<ThreadLocal<Object>, Object> entry : backup.entrySet()) {
final ThreadLocal<Object> threadLocal = entry.getKey();
threadLocal.set(entry.getValue());
}
}
};
private static final Set<Transmittee<Object, Object>> transmitteeSet = new CopyOnWriteArraySet<>();
static {
registerTransmittee(ttlTransmittee);
registerTransmittee(threadLocalTransmittee);
}
/**
* Util method for simplifying {@link #replay(Object)} and {@link #restore(Object)} operations.
*
* @param captured captured {@link TransmittableThreadLocal} values from other thread from {@link #capture()}
* @param bizLogic biz logic
* @param <R> 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> R runSupplierWithCaptured(@NonNull Object captured, @NonNull Supplier<R> bizLogic) {
final Object backup = replay(captured);
try {
return bizLogic.get();
} finally {
restore(backup);
}
}
/**
* Util method for simplifying {@link #clear()} and {@link #restore(Object)} operations.
*
* @param bizLogic biz logic
* @param <R> 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> R runSupplierWithClear(@NonNull Supplier<R> bizLogic) {
final Object backup = clear();
try {
return bizLogic.get();
} finally {
restore(backup);
}
}
/**
* Util method for simplifying {@link #replay(Object)} and {@link #restore(Object)} operations.
*
* @param captured captured {@link TransmittableThreadLocal} values from other thread from {@link #capture()}
* @param bizLogic biz logic
* @param <R> the return type of biz logic
* @return the return value of biz logic
* @throws Exception the exception threw by biz logic
* @see #capture()
* @see #replay(Object)
* @see #restore(Object)
* @since 2.3.1
*/
@SuppressFBWarnings("THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION")
public static <R> R runCallableWithCaptured(@NonNull Object captured, @NonNull Callable<R> bizLogic) throws Exception {
final Object backup = replay(captured);
try {
return bizLogic.call();
} finally {
restore(backup);
}
}
/**
* Util method for simplifying {@link #clear()} and {@link #restore(Object)} operations.
*
* @param bizLogic biz logic
* @param <R> the return type of biz logic
* @return the return value of biz logic
* @throws Exception the exception threw by biz logic
* @see #clear()
* @see #restore(Object)
* @since 2.9.0
*/
@SuppressFBWarnings("THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION")
public static <R> R runCallableWithClear(@NonNull Callable<R> bizLogic) throws Exception {
final Object backup = clear();
try {
return bizLogic.call();
} finally {
restore(backup);
}
}
private static volatile WeakHashMap<ThreadLocal<Object>, TtlCopier<Object>> threadLocalHolder = new WeakHashMap<>();
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 <b>Transmittable</b> ability for the existed {@link ThreadLocal} instances.
* <p>
* 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.
* <p>
* <B><I>Caution:</I></B><br>
* If the registered {@link ThreadLocal} instance is not {@link InheritableThreadLocal},
* the instance can NOT <B><I>{@code inherit}</I></B> value from parent thread(aka. the <b>inheritable</b> ability)!
*
* @param threadLocal the {@link ThreadLocal} instance that to enhance the <b>Transmittable</b> 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 <T> boolean registerThreadLocal(@NonNull ThreadLocal<T> threadLocal, @NonNull TtlCopier<T> copier) {
return registerThreadLocal(threadLocal, copier, false);
}
/**
* Register the {@link ThreadLocal}(including subclass {@link InheritableThreadLocal}) instances
* to enhance the <b>Transmittable</b> ability for the existed {@link ThreadLocal} instances.
* <p>
* Use the shadow copier(transmit the reference directly),
* and should use method {@link #registerThreadLocal(ThreadLocal, TtlCopier)} to pass a customized {@link TtlCopier} explicitly
* if a different behavior is desired.
* <p>
* 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.
* <p>
* <B><I>Caution:</I></B><br>
* If the registered {@link ThreadLocal} instance is not {@link InheritableThreadLocal},
* the instance can NOT <B><I>{@code inherit}</I></B> value from parent thread(aka. the <b>inheritable</b> ability)!
*
* @param threadLocal the {@link ThreadLocal} instance that to enhance the <b>Transmittable</b> 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 <T> boolean registerThreadLocalWithShadowCopier(@NonNull ThreadLocal<T> threadLocal) {
return registerThreadLocal(threadLocal, (TtlCopier<T>) shadowCopier, false);
}
/**
* Register the {@link ThreadLocal}(including subclass {@link InheritableThreadLocal}) instances
* to enhance the <b>Transmittable</b> ability for the existed {@link ThreadLocal} instances.
* <p>
* 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.
* <p>
* <B><I>Caution:</I></B><br>
* If the registered {@link ThreadLocal} instance is not {@link InheritableThreadLocal},
* the instance can NOT <B><I>{@code inherit}</I></B> value from parent thread(aka. the <b>inheritable</b> ability)!
*
* @param threadLocal the {@link ThreadLocal} instance that to enhance the <b>Transmittable</b> 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 <T> boolean registerThreadLocal(@NonNull ThreadLocal<T> threadLocal, @NonNull TtlCopier<T> 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<ThreadLocal<Object>, TtlCopier<Object>> newHolder = new WeakHashMap<>(threadLocalHolder);
newHolder.put((ThreadLocal<Object>) threadLocal, (TtlCopier<Object>) copier);
threadLocalHolder = newHolder;
return true;
}
}
/**
* Register the {@link ThreadLocal}(including subclass {@link InheritableThreadLocal}) instances
* to enhance the <b>Transmittable</b> ability for the existed {@link ThreadLocal} instances.
* <p>
* Use the shadow copier(transmit the reference directly),
* and should use method {@link #registerThreadLocal(ThreadLocal, TtlCopier, boolean)} to pass a customized {@link TtlCopier} explicitly
* if a different behavior is desired.
* <p>
* 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.
* <p>
* <B><I>Caution:</I></B><br>
* If the registered {@link ThreadLocal} instance is not {@link InheritableThreadLocal},
* the instance can NOT <B><I>{@code inherit}</I></B> value from parent thread(aka. the <b>inheritable</b> ability)!
*
* @param threadLocal the {@link ThreadLocal} instance that to enhance the <b>Transmittable</b> 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 <T> boolean registerThreadLocalWithShadowCopier(@NonNull ThreadLocal<T> threadLocal, boolean force) {
return registerThreadLocal(threadLocal, (TtlCopier<T>) shadowCopier, force);
}
/**
* Unregister the {@link ThreadLocal} instances
* to remove the <b>Transmittable</b> ability for the {@link ThreadLocal} instances.
* <p>
* 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 <T> boolean unregisterThreadLocal(@NonNull ThreadLocal<T> 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<ThreadLocal<Object>, TtlCopier<Object>> newHolder = new WeakHashMap<>(threadLocalHolder);
newHolder.remove(threadLocal);
threadLocalHolder = newHolder;
return true;
}
}
private static final TtlCopier<Object> shadowCopier = parentValue -> parentValue;
@SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
private Transmitter() {
throw new InstantiationError("Must not instantiate this class");
}
}
}