Browse Source

Pull request #785: DEC-14578 feat: 引入com.alibaba:transmittable-thread-local

Merge in CORE/base-third from ~ELIJAH/base-third:qufenxi to qufenxi

* commit '8cad028623d000a2f393fe03995c903d2947f74d':
  DEC-14578 打包脚本
  DEC-14578 引入com.alibaba:transmittable-thread-local
qufenxi
Elijah 4 years ago
parent
commit
1ce966913b
  1. 4
      build.third_step8-jdk11.gradle
  2. 4
      build.third_step8.gradle
  3. 41
      fine-transmittable-thread-local/pom.xml
  4. 754
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TransmittableThreadLocal.java
  5. 262
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlCallable.java
  6. 29
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlCopier.java
  7. 15
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlEnhanced.java
  8. 63
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlRecursiveAction.java
  9. 72
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlRecursiveTask.java
  10. 262
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlRunnable.java
  11. 193
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlTimerTask.java
  12. 70
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlUnwrap.java
  13. 342
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/TtlWrappers.java
  14. 9
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/package-info.java
  15. 39
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlAttachments.java
  16. 31
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlAttachmentsDelegate.java
  17. 20
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlEnhanced.java
  18. 26
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/TtlWrapper.java
  19. 9
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/spi/package-info.java
  20. 21
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableForkJoinWorkerThreadFactory.java
  21. 58
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableForkJoinWorkerThreadFactoryWrapper.java
  22. 22
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableThreadFactory.java
  23. 56
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/DisableInheritableThreadFactoryWrapper.java
  24. 101
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/ExecutorServiceTtlWrapper.java
  25. 56
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/ExecutorTtlWrapper.java
  26. 61
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/ScheduledExecutorServiceTtlWrapper.java
  27. 171
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/TtlExecutors.java
  28. 73
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/TtlForkJoinPoolHelper.java
  29. 245
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/TtlAgent.java
  30. 73
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/TtlTransformer.java
  31. 80
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/logging/Logger.java
  32. 66
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/ClassInfo.java
  33. 17
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/JavassistTransformlet.java
  34. 164
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/impl/TtlExecutorTransformlet.java
  35. 85
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/impl/TtlForkJoinTransformlet.java
  36. 71
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/impl/TtlTimerTaskTransformlet.java
  37. 119
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/impl/Utils.java
  38. 7
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/package-info.java
  39. 8
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/package-info.java
  40. 6
      fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/package-info.java

4
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')

4
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}"
}
}

41
fine-transmittable-thread-local/pom.xml

@ -0,0 +1,41 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.fr.third</groupId>
<artifactId>step8</artifactId>
<version>10.0-FEATURE-SNAPSHOT</version>
<relativePath>../base-third-project/base-third-step8</relativePath>
</parent>
<artifactId>fine-transmittable-thread-local</artifactId>
<version>${revision}</version>
<dependencies>
<!--
javassist v3.23 is the last version support Java 6
DO NOT upgrade javassist version!
-->
<!--<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.23.2-GA</version>
<optional>true</optional>
</dependency>-->
<dependency>
<groupId>com.fr.third</groupId>
<artifactId>fine-javassist</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.fr.third</groupId>
<artifactId>fine-jetbrains</artifactId>
<version>${revision}</version>
</dependency>
<!-- Testing frameworks and related dependencies -->
</dependencies>
</project>

754
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.
* <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>
* 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 <b>never</b> happen,
* because threads in thread pooling components is pre-created and pooled, these threads is neutral for biz logic/data.
* <br>
* 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.
* <p>
* or by overriding method {@link #childValue(Object)}.
* 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<String>() {
* 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 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<T> extends InheritableThreadLocal<T> implements TtlCopier<T> {
private static final Logger logger = Logger.getLogger(TransmittableThreadLocal.class.getName());
private final boolean disableIgnoreNullValueSemantics;
/**
* Default constructor.
* <p>
* Create a {@link TransmittableThreadLocal} instance with "Ignore-Null-Value Semantics".
* About "Ignore-Null-Value Semantics":
* <p>
* <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} to biz intent.</li>
* <li>more safe(avoid {@code NPE}) and gc friendly.</li>
* </ol>
* <p>
* So it's 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 set parameter {@code disableIgnoreNullValueSemantics} instead.
* <p>
* More info 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} 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.
* <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() {
}
/**
* 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<TransmittableThreadLocal<Object>, ?>.
// 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<WeakHashMap<TransmittableThreadLocal<Object>, ?>> holder =
new InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>>() {
@Override
protected WeakHashMap<TransmittableThreadLocal<Object>, ?> initialValue() {
return new WeakHashMap<TransmittableThreadLocal<Object>, Object>();
}
@Override
protected WeakHashMap<TransmittableThreadLocal<Object>, ?> childValue(WeakHashMap<TransmittableThreadLocal<Object>, ?> parentValue) {
return new WeakHashMap<TransmittableThreadLocal<Object>, Object>(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) {
for (TransmittableThreadLocal<Object> 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<Object> 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()} =&gt; {@link #replay(Object)} =&gt; {@link #restore(Object)} (aka {@code CRR} operation).
* <p>
* {@link 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 code</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)
* }
* </code></pre>
* <p>
* see the implementation code of {@link TtlRunnable} and {@link TtlCallable} for more actual code sample.
* <p>
* 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}.
* <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, () -&gt; {
* // your biz logic, run with the TransmittableThreadLocal values of thread A
* System.out.println("Hello");
* ...
* return "World";
* }); // (2) + (3)
* </code></pre>
* <p>
* The reason of providing 2 util methods is the different {@code throws Exception} type
* so as 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);
* </code></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
*/
@NotNull
public static Object capture() {
return new Snapshot(captureTtlValues(), captureThreadLocalValues());
}
private static HashMap<TransmittableThreadLocal<Object>, Object> captureTtlValues() {
HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value = new HashMap<TransmittableThreadLocal<Object>, Object>();
for (TransmittableThreadLocal<Object> threadLocal : holder.get().keySet()) {
ttl2Value.put(threadLocal, threadLocal.copyValue());
}
return ttl2Value;
}
private static HashMap<ThreadLocal<Object>, Object> captureThreadLocalValues() {
final HashMap<ThreadLocal<Object>, Object> threadLocal2Value = new HashMap<ThreadLocal<Object>, Object>();
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;
}
/**
* 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<TransmittableThreadLocal<Object>, Object> replayTtlValues(@NotNull HashMap<TransmittableThreadLocal<Object>, Object> captured) {
HashMap<TransmittableThreadLocal<Object>, Object> backup = new HashMap<TransmittableThreadLocal<Object>, Object>();
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;
}
private static HashMap<ThreadLocal<Object>, Object> replayThreadLocalValues(@NotNull HashMap<ThreadLocal<Object>, Object> captured) {
final HashMap<ThreadLocal<Object>, Object> backup = new HashMap<ThreadLocal<Object>, Object>();
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;
}
/**
* 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<TransmittableThreadLocal<Object>, Object> ttl2Value = new HashMap<TransmittableThreadLocal<Object>, Object>();
final HashMap<ThreadLocal<Object>, Object> threadLocal2Value = new HashMap<ThreadLocal<Object>, Object>();
for (Map.Entry<ThreadLocal<Object>, TtlCopier<Object>> entry : threadLocalHolder.entrySet()) {
final ThreadLocal<Object> 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<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(@NotNull 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 void restoreThreadLocalValues(@NotNull 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 class Snapshot {
final HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value;
final HashMap<ThreadLocal<Object>, Object> threadLocal2Value;
private Snapshot(HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value, HashMap<ThreadLocal<Object>, 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 <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(@NotNull Object captured, @NotNull Supplier<R> 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 <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(@NotNull Supplier<R> 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 <R> 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> R runCallableWithCaptured(@NotNull Object captured, @NotNull Callable<R> 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 <R> 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> R runCallableWithClear(@NotNull Callable<R> bizLogic) throws Exception {
Object backup = clear();
try {
return bizLogic.call();
} finally {
restore(backup);
}
}
private static volatile WeakHashMap<ThreadLocal<Object>, TtlCopier<Object>> threadLocalHolder = new WeakHashMap<ThreadLocal<Object>, TtlCopier<Object>>();
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.
*
* @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(@NotNull ThreadLocal<T> threadLocal, @NotNull 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 {@link #registerThreadLocal(ThreadLocal, TtlCopier)} to pass a {@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.
*
* @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(@NotNull 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.
*
* @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(@NotNull ThreadLocal<T> threadLocal, @NotNull 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<ThreadLocal<Object>, TtlCopier<Object>>(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 {@link #registerThreadLocal(ThreadLocal, TtlCopier, boolean)} to pass a {@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.
*
* @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(@NotNull 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(@NotNull 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<ThreadLocal<Object>, TtlCopier<Object>>(threadLocalHolder);
newHolder.remove(threadLocal);
threadLocalHolder = newHolder;
return true;
}
}
private static final TtlCopier<Object> shadowCopier = new TtlCopier<Object>() {
@Override
public Object copy(Object parentValue) {
return parentValue;
}
};
private Transmitter() {
throw new InstantiationError("Must not instantiate this class");
}
}
}

262
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.
* <p>
* Use factory method {@link #get(Callable)} to get decorated instance.
* <p>
* 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<V> implements Callable<V>, TtlWrapper<Callable<V>>, TtlEnhanced, TtlAttachments {
private final AtomicReference<Object> capturedRef;
private final Callable<V> callable;
private final boolean releaseTtlValueReferenceAfterCall;
private TtlCallable(@NotNull Callable<V> callable, boolean releaseTtlValueReferenceAfterCall) {
this.capturedRef = new AtomicReference<Object>(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<V> getCallable() {
return unwrap();
}
/**
* unwrap to the original/underneath {@link Callable}.
*
* @see TtlUnwrap#unwrap(Object)
* @since 2.11.4
*/
@NotNull
@Override
public Callable<V> 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}.
* <p>
* This method is idempotent.
*
* @param callable input {@link Callable}
* @return Wrapped {@link Callable}
*/
@Nullable
public static <T> TtlCallable<T> get(@Nullable Callable<T> callable) {
return get(callable, false);
}
/**
* Factory method, wrap input {@link Callable} to {@link TtlCallable}.
* <p>
* 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 <T> TtlCallable<T> get(@Nullable Callable<T> callable, boolean releaseTtlValueReferenceAfterCall) {
return get(callable, releaseTtlValueReferenceAfterCall, false);
}
/**
* Factory method, wrap input {@link Callable} to {@link TtlCallable}.
* <p>
* 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! <b>DO NOT</b> set, only when you know why.
* @return Wrapped {@link Callable}
*/
@Nullable
public static <T> TtlCallable<T> get(@Nullable Callable<T> callable, boolean releaseTtlValueReferenceAfterCall, boolean idempotent) {
if (null == callable) return null;
if (callable instanceof TtlEnhanced) {
// avoid redundant decoration, and ensure idempotency
if (idempotent) return (TtlCallable<T>) callable;
else throw new IllegalStateException("Already TtlCallable!");
}
return new TtlCallable<T>(callable, releaseTtlValueReferenceAfterCall);
}
/**
* wrap input {@link Callable} Collection to {@link TtlCallable} Collection.
*
* @param tasks task to be wrapped
* @return Wrapped {@link Callable}
*/
@NotNull
public static <T> List<TtlCallable<T>> gets(@Nullable Collection<? extends Callable<T>> 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 <T> List<TtlCallable<T>> gets(@Nullable Collection<? extends Callable<T>> 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! <b>DO NOT</b> set, only when you know why.
* @return Wrapped {@link Callable}
*/
@NotNull
public static <T> List<TtlCallable<T>> gets(@Nullable Collection<? extends Callable<T>> tasks, boolean releaseTtlValueReferenceAfterCall, boolean idempotent) {
if (null == tasks) return Collections.emptyList();
List<TtlCallable<T>> copy = new ArrayList<TtlCallable<T>>();
for (Callable<T> task : tasks) {
copy.add(TtlCallable.get(task, releaseTtlValueReferenceAfterCall, idempotent));
}
return copy;
}
/**
* Unwrap {@link TtlCallable} to the original/underneath one.
* <p>
* 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}.
* <p>
* 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 <T> Callable<T> unwrap(@Nullable Callable<T> callable) {
if (!(callable instanceof TtlCallable)) return callable;
else return ((TtlCallable<T>) callable).getCallable();
}
/**
* Unwrap {@link TtlCallable} to the original/underneath one.
* <p>
* Invoke {@link #unwrap(Callable)} for each element in input collection.
* <p>
* 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 <T> List<Callable<T>> unwraps(@Nullable Collection<? extends Callable<T>> tasks) {
if (null == tasks) return Collections.emptyList();
List<Callable<T>> copy = new ArrayList<Callable<T>>();
for (Callable<T> task : tasks) {
if (!(task instanceof TtlCallable)) copy.add(task);
else copy.add(((TtlCallable<T>) 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> T getTtlAttachment(@NotNull String key) {
return ttlAttachment.getTtlAttachment(key);
}
}

29
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<T> {
/**
* 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.
* <p>
* 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);
}

15
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 {
}

63
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}.
* <p>
* 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<Void> 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);
}
}
}

72
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}.
* <p>
* 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<V> extends ForkJoinTask<V> 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);
}
}
}

262
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.
* <p>
* Use factory methods {@link #get} / {@link #gets} to create instance.
* <p>
* 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<Runnable>, TtlEnhanced, TtlAttachments {
private final AtomicReference<Object> capturedRef;
private final Runnable runnable;
private final boolean releaseTtlValueReferenceAfterRun;
private TtlRunnable(@NotNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
this.capturedRef = new AtomicReference<Object>(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}.
* <B><I>Caution</I></B>: {@code true} will cover up bugs! <b>DO NOT</b> 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<TtlRunnable> 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<TtlRunnable> 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}.
* <B><I>Caution</I></B>: {@code true} will cover up bugs! <b>DO NOT</b> set, only when you know why.
* @return wrapped tasks
* @throws IllegalStateException when input is {@link TtlRunnable} already and not idempotent.
*/
@NotNull
public static List<TtlRunnable> gets(@Nullable Collection<? extends Runnable> tasks, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
if (null == tasks) return Collections.emptyList();
List<TtlRunnable> copy = new ArrayList<TtlRunnable>();
for (Runnable task : tasks) {
copy.add(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent));
}
return copy;
}
/**
* Unwrap {@link TtlRunnable} to the original/underneath one.
* <p>
* 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}.
* <p>
* 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.
* <p>
* Invoke {@link #unwrap(Runnable)} for each element in input collection.
* <p>
* 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<Runnable> unwraps(@Nullable Collection<? extends Runnable> tasks) {
if (null == tasks) return Collections.emptyList();
List<Runnable> copy = new ArrayList<Runnable>();
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> T getTtlAttachment(@NotNull String key) {
return ttlAttachment.getTtlAttachment(key);
}
}

193
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}.
* <p>
* Use factory method {@link #get(TimerTask)} to create instance.
* <p>
* <b>NOTE:</b>
* 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 <a href="https://alibaba.github.io/Alibaba-Java-Coding-Guidelines/#concurrency">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.</a>
* @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<TimerTask>, com.fr.third.alibaba.ttl.spi.TtlEnhanced {
private final AtomicReference<Object> capturedRef;
private final TimerTask timerTask;
private final boolean releaseTtlValueReferenceAfterRun;
private TtlTimerTask(@NotNull TimerTask timerTask, boolean releaseTtlValueReferenceAfterRun) {
this.capturedRef = new AtomicReference<Object>(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}.
* <p>
* 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}.
* <p>
* 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}.
* <p>
* 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! <b>DO NOT</b> 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.
* <p>
* 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.
* <p>
* Invoke {@link #unwrap(TimerTask)} for each element in input collection.
* <p>
* 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<TimerTask> unwraps(@Nullable Collection<? extends TimerTask> tasks) {
if (null == tasks) return Collections.emptyList();
List<TimerTask> copy = new ArrayList<TimerTask>();
for (TimerTask task : tasks) {
if (!(task instanceof TtlTimerTask)) copy.add(task);
else copy.add(((TtlTimerTask) task).getTimerTask());
}
return copy;
}
}

70
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.
* <p>
* <b><i>Note:</i></b>
* all methods is {@code null}-safe, when input parameter is {@code null}, return {@code null}.
* <p>
* <b><i>Implementation Note:</i></b>
* The util methods in this class should have been inside {@link TtlWrappers}.<br>
* 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.
* <p>
* 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.
* <p>
* 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> T unwrap(@Nullable T obj) {
if (!isWrapper(obj)) return obj;
else return ((TtlWrapper<T>) obj).unwrap();
}
/**
* check the input object is a {@code TtlWrapper} or not.
*
* @since 2.11.4
*/
public static <T> boolean isWrapper(@Nullable T obj) {
return obj instanceof TtlWrapper;
}
private TtlUnwrap() {
throw new InstantiationError("Must not instantiate this class");
}
}

342
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}.
* <p>
* <b><i>Note:</i></b>
* <ul>
* <li>all methods is {@code null}-safe, when input parameter is {@code null}, return {@code null}.</li>
* <li>all wrap method skip wrap (aka. just return input parameter), when input parameter is already wrapped.</li>
* </ul>
*
* @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 <T> Supplier<T> wrap(@Nullable Supplier<T> supplier) {
if (supplier == null) return null;
else if (supplier instanceof TtlEnhanced) return supplier;
else return new TtlSupplier<T>(supplier);
}
private static class TtlSupplier<T> implements Supplier<T>, TtlWrapper<Supplier<T>>, TtlEnhanced {
final Supplier<T> supplier;
final Object captured;
TtlSupplier(@NotNull Supplier<T> 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<T> 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 <T> Consumer<T> wrap(@Nullable Consumer<T> consumer) {
if (consumer == null) return null;
else if (consumer instanceof TtlEnhanced) return consumer;
else return new TtlConsumer<T>(consumer);
}
private static class TtlConsumer<T> implements Consumer<T>, TtlWrapper<Consumer<T>>, TtlEnhanced {
final Consumer<T> consumer;
final Object captured;
TtlConsumer(@NotNull Consumer<T> 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<T> 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 <T, U> BiConsumer<T, U> wrap(@Nullable BiConsumer<T, U> consumer) {
if (consumer == null) return null;
else if (consumer instanceof TtlEnhanced) return consumer;
else return new TtlBiConsumer<T, U>(consumer);
}
private static class TtlBiConsumer<T, U> implements BiConsumer<T, U>, TtlWrapper<BiConsumer<T, U>>, TtlEnhanced {
final BiConsumer<T, U> consumer;
final Object captured;
TtlBiConsumer(@NotNull BiConsumer<T, U> 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<T, U> 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 <T, R> Function<T, R> wrap(@Nullable Function<T, R> fn) {
if (fn == null) return null;
else if (fn instanceof TtlEnhanced) return fn;
else return new TtlFunction<T, R>(fn);
}
private static class TtlFunction<T, R> implements Function<T, R>, TtlWrapper<Function<T, R>>, TtlEnhanced {
final Function<T, R> fn;
final Object captured;
TtlFunction(@NotNull Function<T, R> 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<T, R> 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 <T, U, R> BiFunction<T, U, R> wrap(@Nullable BiFunction<T, U, R> fn) {
if (fn == null) return null;
else if (fn instanceof TtlEnhanced) return fn;
else return new TtlBiFunction<T, U, R>(fn);
}
private static class TtlBiFunction<T, U, R> implements BiFunction<T, U, R>, TtlWrapper<BiFunction<T, U, R>>, TtlEnhanced {
final BiFunction<T, U, R> fn;
final Object captured;
TtlBiFunction(@NotNull BiFunction<T, U, R> 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<T, U, R> 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");
}
}

9
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;

39
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> T getTtlAttachment(@NotNull String key);
/**
* The attachment key of TTL task, weather this task is a auto wrapper task.
* <p>
* so the value of this attachment is a {@code boolean}.
*
* @since 2.11.0
*/
String KEY_IS_AUTO_WRAPPER = "ttl.is.auto.wrapper";
}

31
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<String, Object> attachments = new ConcurrentHashMap<String, Object>();
@Override
public void setTtlAttachment(@NotNull String key, Object value) {
attachments.put(key, value);
}
@Override
@SuppressWarnings("unchecked")
public <T> T getTtlAttachment(@NotNull String key) {
return (T) attachments.get(key);
}
}

20
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 {
}

26
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<T> extends TtlEnhanced {
/**
* unwrap {@link TtlWrapper} to the original/underneath one.
* <p>
* 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.
* <p>
* so {@code unwrap} will always return the same input object.
*
* @see TtlUnwrap#unwrap(Object)
*/
@NotNull
T unwrap();
}

9
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;

21
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<ForkJoinWorkerThreadFactory> {
/**
* Unwrap {@link DisableInheritableThreadFactory} to the original/underneath one.
*/
@Override
@NotNull
ForkJoinWorkerThreadFactory unwrap();
}

58
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();
}
}

22
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<ThreadFactory> {
/**
* Unwrap {@link DisableInheritableThreadFactory} to the original/underneath one.
*/
@Override
@NotNull
ThreadFactory unwrap();
}

56
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();
}
}

101
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<Runnable> 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 <T> Future<T> submit(@NotNull Callable<T> task) {
return executorService.submit(TtlCallable.get(task));
}
@NotNull
@Override
public <T> Future<T> 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 <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks) throws InterruptedException {
return executorService.invokeAll(TtlCallable.gets(tasks));
}
@NotNull
@Override
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException {
return executorService.invokeAll(TtlCallable.gets(tasks), timeout, unit);
}
@NotNull
@Override
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
return executorService.invokeAny(TtlCallable.gets(tasks));
}
@Override
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return executorService.invokeAny(TtlCallable.gets(tasks), timeout, unit);
}
@NotNull
@Override
public ExecutorService unwrap() {
return executorService;
}
}

56
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/ExecutorTtlWrapper.java

@ -0,0 +1,56 @@
package com.fr.third.alibaba.ttl.threadpool;
import com.fr.third.alibaba.ttl.TransmittableThreadLocal;
import com.fr.third.alibaba.ttl.TtlRunnable;
import com.fr.third.alibaba.ttl.spi.TtlEnhanced;
import com.fr.third.alibaba.ttl.spi.TtlWrapper;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.Executor;
/**
* {@link TransmittableThreadLocal} Wrapper of {@link Executor},
* transmit the {@link TransmittableThreadLocal} from the task submit time of {@link Runnable}
* to the execution time of {@link Runnable}.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @since 0.9.0
*/
class ExecutorTtlWrapper implements Executor, TtlWrapper<Executor>, TtlEnhanced {
private final Executor executor;
ExecutorTtlWrapper(@NotNull Executor executor) {
this.executor = executor;
}
@Override
public void execute(@NotNull Runnable command) {
executor.execute(TtlRunnable.get(command));
}
@Override
@NotNull
public Executor unwrap() {
return executor;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ExecutorTtlWrapper that = (ExecutorTtlWrapper) o;
return executor.equals(that.executor);
}
@Override
public int hashCode() {
return executor.hashCode();
}
@Override
public String toString() {
return this.getClass().getName() + " - " + executor.toString();
}
}

61
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/ScheduledExecutorServiceTtlWrapper.java

@ -0,0 +1,61 @@
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.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* {@link TransmittableThreadLocal} Wrapper of {@link ScheduledExecutorService},
* 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 ScheduledExecutorServiceTtlWrapper extends ExecutorServiceTtlWrapper implements ScheduledExecutorService, TtlEnhanced {
final ScheduledExecutorService scheduledExecutorService;
public ScheduledExecutorServiceTtlWrapper(@NotNull ScheduledExecutorService scheduledExecutorService) {
super(scheduledExecutorService);
this.scheduledExecutorService = scheduledExecutorService;
}
@NotNull
@Override
public ScheduledFuture<?> schedule(@NotNull Runnable command, long delay, @NotNull TimeUnit unit) {
return scheduledExecutorService.schedule(TtlRunnable.get(command), delay, unit);
}
@NotNull
@Override
public <V> ScheduledFuture<V> schedule(@NotNull Callable<V> callable, long delay, @NotNull TimeUnit unit) {
return scheduledExecutorService.schedule(TtlCallable.get(callable), delay, unit);
}
@NotNull
@Override
public ScheduledFuture<?> scheduleAtFixedRate(@NotNull Runnable command, long initialDelay, long period, @NotNull TimeUnit unit) {
return scheduledExecutorService.scheduleAtFixedRate(TtlRunnable.get(command), initialDelay, period, unit);
}
@NotNull
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(@NotNull Runnable command, long initialDelay, long delay, @NotNull TimeUnit unit) {
return scheduledExecutorService.scheduleWithFixedDelay(TtlRunnable.get(command), initialDelay, delay, unit);
}
@Override
@NotNull
public ScheduledExecutorService unwrap() {
return scheduledExecutorService;
}
}

171
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/TtlExecutors.java

@ -0,0 +1,171 @@
package com.fr.third.alibaba.ttl.threadpool;
import com.fr.third.alibaba.ttl.TransmittableThreadLocal;
import com.fr.third.alibaba.ttl.spi.TtlEnhanced;
import com.fr.third.alibaba.ttl.threadpool.agent.TtlAgent;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.*;
/**
* Util methods for TTL wrapper of jdk executors.
*
* <ol>
* <li>Factory methods to get TTL wrapper from jdk executors.</li>
* <li>unwrap/check methods for TTL wrapper of jdk executors.</li>
* <li>wrap/unwrap/check methods to disable Inheritable for {@link ThreadFactory}.</li>
* </ol>
* <p>
* <b><i>Note:</i></b>
* <ul>
* <li>all method is {@code null}-safe, when input {@code executor} parameter is {@code null}, return {@code null}.</li>
* <li>skip wrap/decoration thread pool/{@code executor}(aka. just return input {@code executor})
* when ttl agent is loaded, Or when input {@code executor} is already wrapped/decorated.</li>
* </ul>
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @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
* @see ThreadFactory
* @see Executors#defaultThreadFactory()
* @since 0.9.0
*/
public final class TtlExecutors {
/**
* {@link TransmittableThreadLocal} Wrapper of {@link Executor},
* transmit the {@link TransmittableThreadLocal} from the task submit time of {@link Runnable}
* to the execution time of {@link Runnable}.
*/
@Nullable
public static Executor getTtlExecutor(@Nullable Executor executor) {
if (TtlAgent.isTtlAgentLoaded() || null == executor || executor instanceof TtlEnhanced) {
return executor;
}
return new ExecutorTtlWrapper(executor);
}
/**
* {@link TransmittableThreadLocal} Wrapper of {@link ExecutorService},
* transmit the {@link TransmittableThreadLocal} from the task submit time of {@link Runnable} or {@link java.util.concurrent.Callable}
* to the execution time of {@link Runnable} or {@link java.util.concurrent.Callable}.
*/
@Nullable
public static ExecutorService getTtlExecutorService(@Nullable ExecutorService executorService) {
if (TtlAgent.isTtlAgentLoaded() || executorService == null || executorService instanceof TtlEnhanced) {
return executorService;
}
return new ExecutorServiceTtlWrapper(executorService);
}
/**
* {@link TransmittableThreadLocal} Wrapper of {@link ScheduledExecutorService},
* transmit the {@link TransmittableThreadLocal} from the task submit time of {@link Runnable} or {@link java.util.concurrent.Callable}
* to the execution time of {@link Runnable} or {@link java.util.concurrent.Callable}.
*/
@Nullable
public static ScheduledExecutorService getTtlScheduledExecutorService(@Nullable ScheduledExecutorService scheduledExecutorService) {
if (TtlAgent.isTtlAgentLoaded() || scheduledExecutorService == null || scheduledExecutorService instanceof TtlEnhanced) {
return scheduledExecutorService;
}
return new ScheduledExecutorServiceTtlWrapper(scheduledExecutorService);
}
/**
* check the executor is TTL wrapper executor or not.
* <p>
* if the parameter executor is TTL wrapper, return {@code true}, otherwise {@code false}.
* <p>
* NOTE: if input executor is {@code null}, return {@code false}.
*
* @param executor input executor
* @param <T> Executor type
* @see #getTtlExecutor(Executor)
* @see #getTtlExecutorService(ExecutorService)
* @see #getTtlScheduledExecutorService(ScheduledExecutorService)
* @see #unwrap(Executor)
* @since 2.8.0
*/
public static <T extends Executor> boolean isTtlWrapper(@Nullable T executor) {
return executor instanceof TtlEnhanced;
}
/**
* Unwrap TTL wrapper executor to the original/underneath one.
* <p>
* if the parameter executor is TTL wrapper, return the original/underneath executor;
* otherwise, just return the input parameter executor.
* <p>
* NOTE: if input executor is {@code null}, return {@code null}.
*
* @param executor input executor
* @param <T> Executor type
* @see #getTtlExecutor(Executor)
* @see #getTtlExecutorService(ExecutorService)
* @see #getTtlScheduledExecutorService(ScheduledExecutorService)
* @see #isTtlWrapper(Executor)
* @since 2.8.0
*/
@Nullable
@SuppressWarnings("unchecked")
public static <T extends Executor> T unwrap(@Nullable T executor) {
if (!isTtlWrapper(executor)) return executor;
return (T) ((ExecutorTtlWrapper) executor).unwrap();
}
/**
* Wrapper of {@link ThreadFactory}, disable inheritable.
*
* @param threadFactory input thread factory
* @see DisableInheritableThreadFactory
* @since 2.10.0
*/
@Nullable
public static ThreadFactory getDisableInheritableThreadFactory(@Nullable ThreadFactory threadFactory) {
if (threadFactory == null || isDisableInheritableThreadFactory(threadFactory)) return threadFactory;
return new DisableInheritableThreadFactoryWrapper(threadFactory);
}
/**
* Wrapper of {@link Executors#defaultThreadFactory()}, disable inheritable.
*
* @see #getDisableInheritableThreadFactory(ThreadFactory)
* @since 2.10.0
*/
@Nullable
public static ThreadFactory getDefaultDisableInheritableThreadFactory() {
return getDisableInheritableThreadFactory(Executors.defaultThreadFactory());
}
/**
* check the {@link ThreadFactory} is {@link DisableInheritableThreadFactory} or not.
*
* @see DisableInheritableThreadFactory
* @since 2.10.0
*/
public static boolean isDisableInheritableThreadFactory(@Nullable ThreadFactory threadFactory) {
return threadFactory instanceof DisableInheritableThreadFactory;
}
/**
* Unwrap {@link DisableInheritableThreadFactory} to the original/underneath one.
*
* @see DisableInheritableThreadFactory
* @since 2.10.0
*/
@Nullable
public static ThreadFactory unwrap(@Nullable ThreadFactory threadFactory) {
if (!isDisableInheritableThreadFactory(threadFactory)) return threadFactory;
return ((DisableInheritableThreadFactory) threadFactory).unwrap();
}
private TtlExecutors() {
}
}

73
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/TtlForkJoinPoolHelper.java

@ -0,0 +1,73 @@
package com.fr.third.alibaba.ttl.threadpool;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory;
/**
* Util methods to wrap/unwrap/check methods to disable Inheritable for {@link ForkJoinWorkerThreadFactory}.
* <p>
* <b><i>Note:</i></b>
* <p>
* all method is {@code null}-safe, when input parameter(eg: {@link ForkJoinWorkerThreadFactory}) is {@code null}, return {@code null}.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @see ForkJoinPool
* @see ForkJoinWorkerThreadFactory
* @see ForkJoinPool#defaultForkJoinWorkerThreadFactory
* @since 2.10.1
*/
public class TtlForkJoinPoolHelper {
/**
* Wrapper of {@link ForkJoinWorkerThreadFactory}, disable inheritable.
*
* @param threadFactory input thread factory
* @see DisableInheritableForkJoinWorkerThreadFactory
* @since 2.10.1
*/
@Nullable
public static ForkJoinWorkerThreadFactory getDisableInheritableForkJoinWorkerThreadFactory(@Nullable ForkJoinWorkerThreadFactory threadFactory) {
if (threadFactory == null || isDisableInheritableForkJoinWorkerThreadFactory(threadFactory))
return threadFactory;
return new DisableInheritableForkJoinWorkerThreadFactoryWrapper(threadFactory);
}
/**
* Wrapper of {@link ForkJoinPool#defaultForkJoinWorkerThreadFactory}, disable inheritable.
*
* @see #getDisableInheritableForkJoinWorkerThreadFactory(ForkJoinWorkerThreadFactory)
* @since 2.10.1
*/
@Nullable
public static ForkJoinWorkerThreadFactory getDefaultDisableInheritableForkJoinWorkerThreadFactory() {
return getDisableInheritableForkJoinWorkerThreadFactory(ForkJoinPool.defaultForkJoinWorkerThreadFactory);
}
/**
* check the {@link ForkJoinWorkerThreadFactory} is {@link DisableInheritableForkJoinWorkerThreadFactory} or not.
*
* @see DisableInheritableForkJoinWorkerThreadFactory
* @since 2.10.1
*/
public static boolean isDisableInheritableForkJoinWorkerThreadFactory(@Nullable ForkJoinWorkerThreadFactory threadFactory) {
return threadFactory instanceof DisableInheritableForkJoinWorkerThreadFactory;
}
/**
* Unwrap {@link DisableInheritableForkJoinWorkerThreadFactory} to the original/underneath one.
*
* @see DisableInheritableForkJoinWorkerThreadFactory
* @since 2.10.1
*/
@Nullable
public static ForkJoinWorkerThreadFactory unwrap(@Nullable ForkJoinWorkerThreadFactory threadFactory) {
if (!isDisableInheritableForkJoinWorkerThreadFactory(threadFactory)) return threadFactory;
return ((DisableInheritableForkJoinWorkerThreadFactory) threadFactory).unwrap();
}
private TtlForkJoinPoolHelper() {
}
}

245
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/TtlAgent.java

@ -0,0 +1,245 @@
package com.fr.third.alibaba.ttl.threadpool.agent;
import com.fr.third.alibaba.ttl.TransmittableThreadLocal;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.logging.Logger;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.JavassistTransformlet;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.impl.TtlExecutorTransformlet;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.impl.TtlForkJoinTransformlet;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.impl.TtlTimerTaskTransformlet;
import com.fr.third.alibaba.ttl.threadpool.DisableInheritableForkJoinWorkerThreadFactory;
import com.fr.third.alibaba.ttl.threadpool.DisableInheritableThreadFactory;
import com.fr.third.alibaba.ttl.threadpool.TtlExecutors;
import com.fr.third.alibaba.ttl.threadpool.TtlForkJoinPoolHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
/**
* TTL Java Agent.
* <p>
* The configuration/arguments for agent see the javadoc of {@link #premain(String, Instrumentation)}
* <p>
* <b><i>NOTE:</i></b><br>
* Since {@code v2.6.0}, TTL agent jar will auto add self to {@code boot classpath}.
* But you <b>should <i>NOT</i></b> modify the downloaded TTL jar file name in the maven repo(eg: {@code transmittable-thread-local-2.x.x.jar}).<br>
* if you modified the downloaded TTL agent jar file name(eg: {@code ttl-foo-name-changed.jar}),
* you must add TTL agent jar to {@code boot classpath} manually
* by java option {@code -Xbootclasspath/a:path/to/ttl-foo-name-changed.jar}.
* <p>
* The implementation of auto adding self agent jar to {@code boot classpath} use
* the {@code Boot-Class-Path} property of manifest file({@code META-INF/MANIFEST.MF}) in the TTL Java Agent Jar:
* <blockquote>
* <dl>
* <dt>Boot-Class-Path</dt>
* <dd>
* A list of paths to be searched by the bootstrap class loader. Paths represent directories or libraries (commonly referred to as JAR or zip libraries on many platforms).
* These paths are searched by the bootstrap class loader after the platform specific mechanisms of locating a class have failed. Paths are searched in the order listed.
* </dd>
* </dl>
* </blockquote>
* <p>
* More info about {@code Boot-Class-Path} see
* <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/instrument/package-summary.html">The mechanism for instrumentation</a>.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @see TransmittableThreadLocal
* @see Instrumentation
* @see <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/instrument/package-summary.html">The mechanism for instrumentation</a>
* @see <a href="https://docs.oracle.com/javase/10/docs/specs/jar/jar.html#jar-manifest">JAR File Specification - JAR Manifest</a>
* @see <a href="https://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html">Working with Manifest Files - The Java TutorialsHide</a>
* @since 0.9.0
*/
public final class TtlAgent {
/**
* Entrance method of TTL Java Agent.
*
* <h3>TTL Agent configuration</h3>
* Configure TTL agent via agent arguments, format is {@code k1:v1,k2:v2}. Below is available configuration keys.
*
* <h3>Disable inheritable for thread pool</h3>
* <p>
* Enable "disable inheritable" for thread pool, config by key {@code ttl.agent.disable.inheritable.for.thread.pool}.
* When no configuration for this key, default does <b>not</b> enabled. Since version {@code 2.10.1}.
*
* <ul>
* <li>rewrite the {@link java.util.concurrent.ThreadFactory} constructor parameter
* of {@link java.util.concurrent.ThreadPoolExecutor}
* to {@link DisableInheritableThreadFactory}
* by util method {@link TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory)}.
* </li>
* <li>rewrite the {@link java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory} constructor parameter
* of {@link java.util.concurrent.ForkJoinPool}
* to {@link DisableInheritableForkJoinWorkerThreadFactory}
* by util method {@link TtlForkJoinPoolHelper#getDisableInheritableForkJoinWorkerThreadFactory(java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory)}.
* </li>
* </ul>
* More info about "disable inheritable" see {@link TransmittableThreadLocal}.
* <p>
* Configuration example:<br>
* {@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.disable.inheritable.for.thread.pool:true}
*
* <h3>The log configuration</h3>
* The log of TTL Java Agent is config by key {@code ttl.agent.logger}. Since version {@code 2.6.0}.
*
* <ul>
* <li>{@code ttl.agent.logger : STDERR}<br>
* only log to {@code stderr} when error.
* This is <b>default</b>, when no/unrecognized configuration for key {@code ttl.agent.logger}.</li>
* <li>{@code ttl.agent.logger : STDOUT}<br>
* Log to {@code stdout}, more info than {@code ttl.agent.logger:STDERR}; This is needed when developing.</li>
* </ul>
* <p>
* configuration example:
* <ul>
* <li>{@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar}</li>
* <li>{@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.logger:STDOUT}</li>
* </ul>
*
* <h3>Enable/disable TimerTask class decoration</h3>
* Enable/disable TimerTask class decoration is config by key {@code ttl.agent.enable.timer.task}.
* Since version {@code 2.7.0}.
* <p>
* When no configuration for this key, default is <b>enabled</b>.<br>
* <b><i>Note</i></b>: Since version {@code 2.11.2} the default value is {@code true}(enable TimerTask class decoration);
* Before version {@code 2.11.1} default value is {@code false}.
* <p>
* Configuration example:
* <ul>
* <li>{@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.enable.timer.task:false}</li>
* <li>{@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.enable.timer.task:true}</li>
* </ul>
*
* <h3>Multi key configuration example</h3>
* {@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.logger:STDOUT,ttl.agent.disable.inheritable.for.thread.pool:true}
*
* @see java.util.concurrent.ThreadPoolExecutor
* @see java.util.concurrent.ScheduledThreadPoolExecutor
* @see java.util.concurrent.ForkJoinPool
* @see java.util.TimerTask
* @see Logger
* @see Logger#TTL_AGENT_LOGGER_KEY
* @see Logger#STDERR
* @see Logger#STDOUT
*/
public static void premain(String agentArgs, @NotNull Instrumentation inst) {
kvs = splitCommaColonStringToKV(agentArgs);
Logger.setLoggerImplType(getLogImplTypeFromAgentArgs(kvs));
final Logger logger = Logger.getLogger(TtlAgent.class);
try {
logger.info("[TtlAgent.premain] begin, agentArgs: " + agentArgs + ", Instrumentation: " + inst);
final boolean disableInheritableForThreadPool = isDisableInheritableForThreadPool();
final List<JavassistTransformlet> transformletList = new ArrayList<JavassistTransformlet>();
transformletList.add(new TtlExecutorTransformlet(disableInheritableForThreadPool));
transformletList.add(new TtlForkJoinTransformlet(disableInheritableForThreadPool));
if (isEnableTimerTask()) transformletList.add(new TtlTimerTaskTransformlet());
final ClassFileTransformer transformer = new TtlTransformer(transformletList);
inst.addTransformer(transformer, true);
logger.info("[TtlAgent.premain] addTransformer " + transformer.getClass() + " success");
logger.info("[TtlAgent.premain] end");
ttlAgentLoaded = true;
} catch (Exception e) {
String msg = "Fail to load TtlAgent , cause: " + e.toString();
logger.log(Level.SEVERE, msg, e);
throw new IllegalStateException(msg, e);
}
}
private static String getLogImplTypeFromAgentArgs(@NotNull final Map<String, String> kvs) {
return kvs.get(Logger.TTL_AGENT_LOGGER_KEY);
}
private static volatile Map<String, String> kvs;
private static volatile boolean ttlAgentLoaded = false;
/**
* Whether TTL agent is loaded.
*
* @since 2.9.0
*/
public static boolean isTtlAgentLoaded() {
return ttlAgentLoaded;
}
private static final String TTL_AGENT_ENABLE_TIMER_TASK_KEY = "ttl.agent.enable.timer.task";
private static final String TTL_AGENT_DISABLE_INHERITABLE_FOR_THREAD_POOL = "ttl.agent.disable.inheritable.for.thread.pool";
/**
* Whether disable inheritable for thread pool is enhanced by ttl agent, check {@link #isTtlAgentLoaded()} first.
*
* @see TtlExecutors#getDefaultDisableInheritableThreadFactory()
* @see TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory)
* @see DisableInheritableThreadFactory
* @see TtlForkJoinPoolHelper#getDefaultDisableInheritableForkJoinWorkerThreadFactory()
* @see TtlForkJoinPoolHelper#getDisableInheritableForkJoinWorkerThreadFactory(java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory)
* @see DisableInheritableForkJoinWorkerThreadFactory
* @since 2.10.1
*/
public static boolean isDisableInheritableForThreadPool() {
return isOptionSetOrFalse(kvs, TTL_AGENT_DISABLE_INHERITABLE_FOR_THREAD_POOL);
}
/**
* Whether timer task is enhanced by ttl agent, check {@link #isTtlAgentLoaded()} first.
*
* @since 2.10.1
*/
public static boolean isEnableTimerTask() {
return isOptionSetOrTrue(kvs, TTL_AGENT_ENABLE_TIMER_TASK_KEY);
}
private static boolean isOptionSetOrFalse(@Nullable final Map<String, String> kvs, @NotNull String key) {
return isOptionSetOrFalse(kvs, key, false);
}
private static boolean isOptionSetOrTrue(@Nullable final Map<String, String> kvs, @NotNull String key) {
return isOptionSetOrFalse(kvs, key, true);
}
private static boolean isOptionSetOrFalse(@Nullable final Map<String, String> kvs, @NotNull String key, boolean defaultValue) {
if (null == kvs) return defaultValue;
final boolean hasEnableKey = kvs.containsKey(key);
if (!hasEnableKey) return defaultValue;
return !"false".equalsIgnoreCase(kvs.get(key));
}
/**
* Split to {@code json} like String({@code "k1:v1,k2:v2"}) to KV map({@code "k1"->"v1", "k2"->"v2"}).
*/
@NotNull
static Map<String, String> splitCommaColonStringToKV(@Nullable String commaColonString) {
Map<String, String> ret = new HashMap<String, String>();
if (commaColonString == null || commaColonString.trim().length() == 0) return ret;
final String[] splitKvArray = commaColonString.trim().split("\\s*,\\s*");
for (String kvString : splitKvArray) {
final String[] kv = kvString.trim().split("\\s*:\\s*");
if (kv.length == 0) continue;
if (kv.length == 1) ret.put(kv[0], "");
else ret.put(kv[0], kv[1]);
}
return ret;
}
private TtlAgent() {
throw new InstantiationError("Must not instantiate this class");
}
}

73
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/TtlTransformer.java

@ -0,0 +1,73 @@
package com.fr.third.alibaba.ttl.threadpool.agent;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.logging.Logger;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.ClassInfo;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.JavassistTransformlet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
/**
* TTL {@link ClassFileTransformer} of Java Agent
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @see ClassFileTransformer
* @see <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/instrument/package-summary.html">The mechanism for instrumentation</a>
* @since 0.9.0
*/
public class TtlTransformer implements ClassFileTransformer {
private static final Logger logger = Logger.getLogger(TtlTransformer.class);
/**
* "<code>null</code> if no transform is performed",
* see {@code @return} of {@link ClassFileTransformer#transform(ClassLoader, String, Class, ProtectionDomain, byte[])}
*/
// [ERROR] com.alibaba.ttl.threadpool.agent.TtlTransformer.transform(ClassLoader, String, Class, ProtectionDomain, byte[])
// may expose internal representation by returning TtlTransformer.NO_TRANSFORM
// the value is null, so there is NO "EI_EXPOSE_REP" problem actually.
private static final byte[] NO_TRANSFORM = null;
private final List<JavassistTransformlet> transformletList = new ArrayList<JavassistTransformlet>();
TtlTransformer(List<? extends JavassistTransformlet> transformletList) {
for (JavassistTransformlet transformlet : transformletList) {
this.transformletList.add(transformlet);
logger.info("[TtlTransformer] add Transformlet " + transformlet.getClass() + " success");
}
}
@Override
public final byte[] transform(@Nullable final ClassLoader loader, @Nullable final String classFile, final Class<?> classBeingRedefined,
final ProtectionDomain protectionDomain, @NotNull final byte[] classFileBuffer) {
try {
// Lambda has no class file, no need to transform, just return.
if (classFile == null) return NO_TRANSFORM;
final String className = toClassName(classFile);
ClassInfo classInfo = new ClassInfo(className, classFileBuffer, loader);
for (JavassistTransformlet transformlet : transformletList) {
transformlet.doTransform(classInfo);
if (classInfo.isModified()) return classInfo.getCtClass().toBytecode();
}
} catch (Throwable t) {
String msg = "Fail to transform class " + classFile + ", cause: " + t.toString();
logger.log(Level.SEVERE, msg, t);
throw new IllegalStateException(msg, t);
}
return NO_TRANSFORM;
}
private static String toClassName(@NotNull final String classFile) {
return classFile.replace('/', '.');
}
}

80
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/logging/Logger.java

@ -0,0 +1,80 @@
package com.fr.third.alibaba.ttl.threadpool.agent.internal.logging;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
/**
* logger adaptor for ttl java agent, internal use for ttl usage only!
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @since 2.6.0
*/
public abstract class Logger {
public static final String TTL_AGENT_LOGGER_KEY = "ttl.agent.logger";
public static final String STDOUT = "STDOUT";
public static final String STDERR = "STDERR";
private static volatile int loggerImplType = -1;
public static void setLoggerImplType(String type) {
if (loggerImplType != -1) {
throw new IllegalStateException("TTL logger implementation type is already set! type = " + loggerImplType);
}
if (STDERR.equalsIgnoreCase(type)) loggerImplType = 0;
else if (STDOUT.equalsIgnoreCase(type)) loggerImplType = 1;
else loggerImplType = 0;
}
public static Logger getLogger(Class<?> clazz) {
if (loggerImplType == -1) throw new IllegalStateException("TTL logger implementation type is NOT set!");
switch (loggerImplType) {
case 1:
return new StdOutLogger(clazz);
default:
return new StdErrorLogger(clazz);
}
}
final Class<?> logClass;
private Logger(Class<?> logClass) {
this.logClass = logClass;
}
public void info(String msg) {
log(Level.INFO, msg, null);
}
public abstract void log(Level level, String msg, Throwable thrown);
private static class StdErrorLogger extends Logger {
StdErrorLogger(Class<?> clazz) {
super(clazz);
}
@Override
public void log(Level level, String msg, Throwable thrown) {
if (level == Level.SEVERE) {
final String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
System.err.printf("%s %s [%s] %s: %s%n", time, level, Thread.currentThread().getName(), logClass.getSimpleName(), msg);
if (thrown != null) thrown.printStackTrace();
}
}
}
private static class StdOutLogger extends Logger {
StdOutLogger(Class<?> clazz) {
super(clazz);
}
@Override
public void log(Level level, String msg, Throwable thrown) {
final String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
System.out.printf("%s %s [%s] %s: %s%n", time, level, Thread.currentThread().getName(), logClass.getSimpleName(), msg);
if (thrown != null) thrown.printStackTrace(System.out);
}
}
}

66
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/ClassInfo.java

@ -0,0 +1,66 @@
package com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet;
import com.fr.third.javassist.CtClass;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.fr.third.javassist.ClassPool;
import com.fr.third.javassist.CtClass;
import com.fr.third.javassist.LoaderClassPath;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/**
* @author Jerry Lee (oldratlee at gmail dot com)
* @since 2.11.0
*/
public class ClassInfo {
private final String className;
private final byte[] classFileBuffer;
private final ClassLoader loader;
// SuppressFBWarnings for classFileBuffer parameter:
// [ERROR] new com.alibaba.ttl.threadpool.agent.internal.transformlet.ClassInfo(String, byte[], ClassLoader)
// may expose internal representation by storing an externally mutable object
// into ClassInfo.classFileBuffer
public ClassInfo(@NotNull String className, byte[] classFileBuffer, @Nullable ClassLoader loader) {
this.className = className;
this.classFileBuffer = classFileBuffer;
this.loader = loader;
}
@NotNull
public String getClassName() {
return className;
}
private CtClass ctClass;
@NotNull
public CtClass getCtClass() throws IOException {
if (ctClass != null) return ctClass;
final ClassPool classPool = new ClassPool(true);
if (loader == null) {
classPool.appendClassPath(new LoaderClassPath(ClassLoader.getSystemClassLoader()));
} else {
classPool.appendClassPath(new LoaderClassPath(loader));
}
final CtClass clazz = classPool.makeClass(new ByteArrayInputStream(classFileBuffer), false);
clazz.defrost();
this.ctClass = clazz;
return clazz;
}
private boolean modified = false;
public boolean isModified() {
return modified;
}
public void setModified() {
this.modified = true;
}
}

17
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/JavassistTransformlet.java

@ -0,0 +1,17 @@
package com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet;
import org.jetbrains.annotations.NotNull;
import com.fr.third.javassist.CannotCompileException;
import com.fr.third.javassist.NotFoundException;
import java.io.IOException;
/**
* TTL {@code Transformlet} by {@code Javassist}.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @since 2.5.1
*/
public interface JavassistTransformlet {
void doTransform(@NotNull ClassInfo classInfo) throws IOException, NotFoundException, CannotCompileException;
}

164
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/impl/TtlExecutorTransformlet.java

@ -0,0 +1,164 @@
package com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.impl;
import com.fr.third.alibaba.ttl.threadpool.TtlExecutors;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.logging.Logger;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.ClassInfo;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.JavassistTransformlet;
import com.fr.third.alibaba.ttl.TtlCallable;
import com.fr.third.alibaba.ttl.TtlRunnable;
import org.jetbrains.annotations.NotNull;
import com.fr.third.javassist.*;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
/**
* TTL {@link JavassistTransformlet} for {@link java.util.concurrent.Executor}.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @author wuwen5 (wuwen.55 at aliyun dot com)
* @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 2.5.1
*/
public class TtlExecutorTransformlet implements JavassistTransformlet {
private static final Logger logger = Logger.getLogger(TtlExecutorTransformlet.class);
private static Set<String> EXECUTOR_CLASS_NAMES = new HashSet<String>();
private static final Map<String, String> PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS = new HashMap<String, String>();
private static final String THREAD_POOL_EXECUTOR_CLASS_NAME = "java.util.concurrent.ThreadPoolExecutor";
private static final String RUNNABLE_CLASS_NAME = "java.lang.Runnable";
static {
EXECUTOR_CLASS_NAMES.add(THREAD_POOL_EXECUTOR_CLASS_NAME);
EXECUTOR_CLASS_NAMES.add("java.util.concurrent.ScheduledThreadPoolExecutor");
PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.put(RUNNABLE_CLASS_NAME, "com.alibaba.ttl.TtlRunnable");
PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.put("java.util.concurrent.Callable", "com.alibaba.ttl.TtlCallable");
}
private static final String THREAD_FACTORY_CLASS_NAME = "java.util.concurrent.ThreadFactory";
private final boolean disableInheritableForThreadPool;
public TtlExecutorTransformlet(boolean disableInheritableForThreadPool) {
this.disableInheritableForThreadPool = disableInheritableForThreadPool;
}
@Override
public void doTransform(@NotNull final ClassInfo classInfo) throws IOException, NotFoundException, CannotCompileException {
if (EXECUTOR_CLASS_NAMES.contains(classInfo.getClassName())) {
final CtClass clazz = classInfo.getCtClass();
for (CtMethod method : clazz.getDeclaredMethods()) {
updateSubmitMethodsOfExecutorClass_decorateToTtlWrapperAndSetAutoWrapperAttachment(method);
}
if (disableInheritableForThreadPool) updateConstructorDisableInheritable(clazz);
classInfo.setModified();
} else {
final CtClass clazz = classInfo.getCtClass();
if (clazz.isPrimitive() || clazz.isArray() || clazz.isInterface() || clazz.isAnnotation()) {
return;
}
if (!clazz.subclassOf(clazz.getClassPool().get(THREAD_POOL_EXECUTOR_CLASS_NAME))) return;
logger.info("Transforming class " + classInfo.getClassName());
final boolean modified = updateBeforeAndAfterExecuteMethodOfExecutorSubclass(clazz);
if (modified) classInfo.setModified();
}
}
/**
* @see TtlRunnable#get(Runnable, boolean, boolean)
* @see TtlCallable#get(Callable, boolean, boolean)
* @see Utils#setAutoWrapperAttachment(Object)
*/
// [ERROR] Format string should use %n rather than \n
private void updateSubmitMethodsOfExecutorClass_decorateToTtlWrapperAndSetAutoWrapperAttachment(@NotNull final CtMethod method) throws NotFoundException, CannotCompileException {
final int modifiers = method.getModifiers();
if (!Modifier.isPublic(modifiers) || Modifier.isStatic(modifiers)) return;
CtClass[] parameterTypes = method.getParameterTypes();
StringBuilder insertCode = new StringBuilder();
for (int i = 0; i < parameterTypes.length; i++) {
final String paramTypeName = parameterTypes[i].getName();
if (PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.containsKey(paramTypeName)) {
String code = String.format(
// decorate to TTL wrapper,
// and then set AutoWrapper attachment/Tag
"$%d = %s.get($%d, false, true);"
+ "\ncom.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.setAutoWrapperAttachment($%<d);",
i + 1, PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.get(paramTypeName), i + 1);
logger.info("insert code before method " + Utils.signatureOfMethod(method) + " of class " + method.getDeclaringClass().getName() + ": " + code);
insertCode.append(code);
}
}
if (insertCode.length() > 0) method.insertBefore(insertCode.toString());
}
/**
* @see TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory)
*/
private void updateConstructorDisableInheritable(@NotNull final CtClass clazz) throws NotFoundException, CannotCompileException {
for (CtConstructor constructor : clazz.getDeclaredConstructors()) {
final CtClass[] parameterTypes = constructor.getParameterTypes();
final StringBuilder insertCode = new StringBuilder();
for (int i = 0; i < parameterTypes.length; i++) {
final String paramTypeName = parameterTypes[i].getName();
if (THREAD_FACTORY_CLASS_NAME.equals(paramTypeName)) {
String code = String.format("$%d = com.alibaba.ttl.threadpool.TtlExecutors.getDisableInheritableThreadFactory($%<d);", i + 1);
logger.info("insert code before method " + Utils.signatureOfMethod(constructor) + " of class " + constructor.getDeclaringClass().getName() + ": " + code);
insertCode.append(code);
}
}
if (insertCode.length() > 0) constructor.insertBefore(insertCode.toString());
}
}
/**
* @see Utils#unwrapIfIsAutoWrapper(Runnable)
*/
private boolean updateBeforeAndAfterExecuteMethodOfExecutorSubclass(@NotNull final CtClass clazz) throws NotFoundException, CannotCompileException {
final CtClass runnableClass = clazz.getClassPool().get(RUNNABLE_CLASS_NAME);
final CtClass threadClass = clazz.getClassPool().get("java.lang.Thread");
final CtClass throwableClass = clazz.getClassPool().get("java.lang.Throwable");
boolean modified = false;
try {
final CtMethod beforeExecute = clazz.getDeclaredMethod("beforeExecute", new CtClass[]{threadClass, runnableClass});
// unwrap runnable if IsAutoWrapper
String code = "$2 = com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.unwrapIfIsAutoWrapper($2);";
logger.info("insert code before method " + Utils.signatureOfMethod(beforeExecute) + " of class " + beforeExecute.getDeclaringClass().getName() + ": " + code);
beforeExecute.insertBefore(code);
modified = true;
} catch (NotFoundException e) {
// clazz does not override beforeExecute method, do nothing.
}
try {
final CtMethod afterExecute = clazz.getDeclaredMethod("afterExecute", new CtClass[]{runnableClass, throwableClass});
// unwrap runnable if IsAutoWrapper
String code = "$1 = com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.unwrapIfIsAutoWrapper($1);";
logger.info("insert code before method " + Utils.signatureOfMethod(afterExecute) + " of class " + afterExecute.getDeclaringClass().getName() + ": " + code);
afterExecute.insertBefore(code);
modified = true;
} catch (NotFoundException e) {
// clazz does not override afterExecute method, do nothing.
}
return modified;
}
}

85
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/impl/TtlForkJoinTransformlet.java

@ -0,0 +1,85 @@
package com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.impl;
import com.fr.third.alibaba.ttl.spi.TtlEnhanced;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.logging.Logger;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.ClassInfo;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.JavassistTransformlet;
import org.jetbrains.annotations.NotNull;
import com.fr.third.javassist.*;
import java.io.IOException;
/**
* TTL {@link JavassistTransformlet} for {@link java.util.concurrent.ForkJoinTask}.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @author wuwen5 (wuwen.55 at aliyun dot com)
* @see java.util.concurrent.ForkJoinPool
* @see java.util.concurrent.ForkJoinTask
* @since 2.5.1
*/
public class TtlForkJoinTransformlet implements JavassistTransformlet {
private static final Logger logger = Logger.getLogger(TtlForkJoinTransformlet.class);
private static final String FORK_JOIN_TASK_CLASS_NAME = "java.util.concurrent.ForkJoinTask";
private static final String FORK_JOIN_POOL_CLASS_NAME = "java.util.concurrent.ForkJoinPool";
private static final String FORK_JOIN_WORKER_THREAD_FACTORY_CLASS_NAME = "java.util.concurrent.ForkJoinPool$ForkJoinWorkerThreadFactory";
private final boolean disableInheritableForThreadPool;
public TtlForkJoinTransformlet(boolean disableInheritableForThreadPool) {
this.disableInheritableForThreadPool = disableInheritableForThreadPool;
}
@Override
public void doTransform(@NotNull final ClassInfo classInfo) throws IOException, NotFoundException, CannotCompileException {
if (FORK_JOIN_TASK_CLASS_NAME.equals(classInfo.getClassName())) {
updateForkJoinTaskClass(classInfo.getCtClass());
classInfo.setModified();
} else if (disableInheritableForThreadPool && FORK_JOIN_POOL_CLASS_NAME.equals(classInfo.getClassName())) {
updateConstructorDisableInheritable(classInfo.getCtClass());
classInfo.setModified();
}
}
/**
* @see Utils#doCaptureWhenNotTtlEnhanced(java.lang.Object)
*/
private void updateForkJoinTaskClass(@NotNull final CtClass clazz) throws CannotCompileException, NotFoundException {
final String className = clazz.getName();
// add new field
final String capturedFieldName = "captured$field$added$by$ttl";
final CtField capturedField = CtField.make("private final Object " + capturedFieldName + ";", clazz);
clazz.addField(capturedField, "com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.doCaptureWhenNotTtlEnhanced(this);");
logger.info("add new field " + capturedFieldName + " to class " + className);
final CtMethod doExecMethod = clazz.getDeclaredMethod("doExec", new CtClass[0]);
final String doExec_renamed_method_name = Utils.renamedMethodNameByTtl(doExecMethod);
final String beforeCode = "if (this instanceof " + TtlEnhanced.class.getName() + ") {\n" + // if the class is already TTL enhanced(eg: com.alibaba.ttl.TtlRecursiveTask)
" return " + doExec_renamed_method_name + "($$);\n" + // return directly/do nothing
"}\n" +
"Object backup = com.alibaba.ttl.TransmittableThreadLocal.Transmitter.replay(" + capturedFieldName + ");";
final String finallyCode = "com.alibaba.ttl.TransmittableThreadLocal.Transmitter.restore(backup);";
Utils.doTryFinallyForMethod(doExecMethod, doExec_renamed_method_name, beforeCode, finallyCode);
}
private void updateConstructorDisableInheritable(@NotNull final CtClass clazz) throws NotFoundException, CannotCompileException {
for (CtConstructor constructor : clazz.getDeclaredConstructors()) {
final CtClass[] parameterTypes = constructor.getParameterTypes();
final StringBuilder insertCode = new StringBuilder();
for (int i = 0; i < parameterTypes.length; i++) {
final String paramTypeName = parameterTypes[i].getName();
if (FORK_JOIN_WORKER_THREAD_FACTORY_CLASS_NAME.equals(paramTypeName)) {
String code = String.format("$%d = com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper.getDisableInheritableForkJoinWorkerThreadFactory($%<d);", i + 1);
logger.info("insert code before method " + Utils.signatureOfMethod(constructor) + " of class " + constructor.getDeclaringClass().getName() + ": " + code);
insertCode.append(code);
}
}
if (insertCode.length() > 0) constructor.insertBefore(insertCode.toString());
}
}
}

71
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/impl/TtlTimerTaskTransformlet.java

@ -0,0 +1,71 @@
package com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.impl;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.logging.Logger;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.ClassInfo;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.JavassistTransformlet;
import org.jetbrains.annotations.NotNull;
import com.fr.third.javassist.*;
import java.io.IOException;
import static com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.doTryFinallyForMethod;
/**
* TTL {@link JavassistTransformlet} for {@link java.util.TimerTask}.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @author wuwen5 (wuwen.55 at aliyun dot com)
* @see java.util.TimerTask
* @see java.util.Timer
* @since 2.7.0
*/
public class TtlTimerTaskTransformlet implements JavassistTransformlet {
private static final Logger logger = Logger.getLogger(TtlTimerTaskTransformlet.class);
private static final String TIMER_TASK_CLASS_NAME = "java.util.TimerTask";
private static final String RUN_METHOD_NAME = "run";
@Override
public void doTransform(@NotNull final ClassInfo classInfo) throws IOException, NotFoundException, CannotCompileException {
if (TIMER_TASK_CLASS_NAME.equals(classInfo.getClassName())) return; // No need transform TimerTask class
final CtClass clazz = classInfo.getCtClass();
if (clazz.isPrimitive() || clazz.isArray() || clazz.isInterface() || clazz.isAnnotation()) {
return;
}
// class contains method `void run()` ?
try {
final CtMethod runMethod = clazz.getDeclaredMethod(RUN_METHOD_NAME, new CtClass[0]);
if (!CtClass.voidType.equals(runMethod.getReturnType())) return;
} catch (NotFoundException e) {
return;
}
if (!clazz.subclassOf(clazz.getClassPool().get(TIMER_TASK_CLASS_NAME))) return;
logger.info("Transforming class " + classInfo.getClassName());
updateTimerTaskClass(clazz);
classInfo.setModified();
}
/**
* @see Utils#doCaptureWhenNotTtlEnhanced(java.lang.Object)
*/
private void updateTimerTaskClass(@NotNull final CtClass clazz) throws CannotCompileException, NotFoundException {
final String className = clazz.getName();
// add new field
final String capturedFieldName = "captured$field$added$by$ttl";
final CtField capturedField = CtField.make("private final Object " + capturedFieldName + ";", clazz);
clazz.addField(capturedField, "com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.doCaptureWhenNotTtlEnhanced(this);");
logger.info("add new field " + capturedFieldName + " to class " + className);
final CtMethod runMethod = clazz.getDeclaredMethod(RUN_METHOD_NAME, new CtClass[0]);
final String beforeCode = "Object backup = com.alibaba.ttl.TransmittableThreadLocal.Transmitter.replay(" + capturedFieldName + ");";
final String finallyCode = "com.alibaba.ttl.TransmittableThreadLocal.Transmitter.restore(backup);";
Utils.doTryFinallyForMethod(runMethod, beforeCode, finallyCode);
}
}

119
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/impl/Utils.java

@ -0,0 +1,119 @@
package com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.impl;
import com.fr.third.alibaba.ttl.TtlRunnable;
import com.fr.third.alibaba.ttl.spi.TtlAttachments;
import com.fr.third.alibaba.ttl.spi.TtlEnhanced;
import com.fr.third.alibaba.ttl.threadpool.agent.internal.logging.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.fr.third.javassist.*;
import java.lang.reflect.Modifier;
import static com.fr.third.alibaba.ttl.TransmittableThreadLocal.Transmitter.capture;
/**
* @author Jerry Lee (oldratlee at gmail dot com)
* @since 2.6.0
*/
public class Utils {
private static final Logger logger = Logger.getLogger(Utils.class);
/**
* String like {@code public ScheduledFuture scheduleAtFixedRate(Runnable, long, long, TimeUnit)}
* for {@link java.util.concurrent.ScheduledThreadPoolExecutor#scheduleAtFixedRate}.
*
* @param method method object
* @return method signature string
*/
@NotNull
static String signatureOfMethod(@NotNull final CtBehavior method) throws NotFoundException {
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(Modifier.toString(method.getModifiers()));
if (method instanceof CtMethod) {
final String returnType = ((CtMethod) method).getReturnType().getSimpleName();
stringBuilder.append(" ").append(returnType);
}
stringBuilder.append(" ").append(method.getName()).append("(");
final CtClass[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
CtClass parameterType = parameterTypes[i];
if (i != 0) stringBuilder.append(", ");
stringBuilder.append(parameterType.getSimpleName());
}
stringBuilder.append(")");
return stringBuilder.toString();
}
@NotNull
static String renamedMethodNameByTtl(@NotNull CtMethod method) {
return "original$" + method.getName() + "$method$renamed$by$ttl";
}
static void doTryFinallyForMethod(@NotNull CtMethod method, @NotNull String beforeCode, @NotNull String finallyCode) throws CannotCompileException, NotFoundException {
doTryFinallyForMethod(method, renamedMethodNameByTtl(method), beforeCode, finallyCode);
}
static void doTryFinallyForMethod(@NotNull CtMethod method, @NotNull String renamedMethodName, @NotNull String beforeCode, @NotNull String finallyCode) throws CannotCompileException, NotFoundException {
final CtClass clazz = method.getDeclaringClass();
final CtMethod newMethod = CtNewMethod.copy(method, clazz, null);
// rename original method, and set to private method(avoid reflect out renamed method unexpectedly)
method.setName(renamedMethodName);
method.setModifiers(method.getModifiers()
& ~Modifier.PUBLIC /* remove public */
& ~Modifier.PROTECTED /* remove protected */
| Modifier.PRIVATE /* add private */);
final String returnOp;
if (method.getReturnType() == CtClass.voidType) {
returnOp = "";
} else {
returnOp = "return ";
}
// set new method implementation
final String code = "{\n" +
beforeCode + "\n" +
"try {\n" +
" " + returnOp + renamedMethodName + "($$);\n" +
"} finally {\n" +
" " + finallyCode + "\n" +
"} }";
newMethod.setBody(code);
clazz.addMethod(newMethod);
logger.info("insert code around method " + signatureOfMethod(newMethod) + " of class " + clazz.getName() + ": " + code);
}
@Nullable
public static Object doCaptureWhenNotTtlEnhanced(@Nullable Object obj) {
if (obj instanceof TtlEnhanced) return null;
else return capture();
}
public static void setAutoWrapperAttachment(@Nullable Object ttlAttachment) {
if (notTtlAttachments(ttlAttachment)) return;
((TtlAttachments) ttlAttachment).setTtlAttachment(TtlAttachments.KEY_IS_AUTO_WRAPPER, true);
}
@Nullable
public static Runnable unwrapIfIsAutoWrapper(@Nullable Runnable runnable) {
if (isAutoWrapper(runnable)) return TtlRunnable.unwrap(runnable);
else return runnable;
}
private static boolean notTtlAttachments(@Nullable Object ttlAttachment) {
return !(ttlAttachment instanceof TtlAttachments);
}
private static boolean isAutoWrapper(@Nullable Runnable ttlAttachments) {
if (notTtlAttachments(ttlAttachments)) return false;
final Boolean value = ((TtlAttachments) ttlAttachments).getTtlAttachment(TtlAttachments.KEY_IS_AUTO_WRAPPER);
if (value == null) return false;
return value;
}
}

7
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/internal/transformlet/package-info.java

@ -0,0 +1,7 @@
/**
* TTL {@code Transformlet} implementations by {@code Javassist}.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @see com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.JavassistTransformlet
*/
package com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet;

8
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/agent/package-info.java

@ -0,0 +1,8 @@
/**
* TTL Agent.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @see com.fr.third.alibaba.ttl.threadpool.agent.TtlAgent
* @see <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/instrument/package-summary.html">The mechanism for instrumentation</a>
*/
package com.fr.third.alibaba.ttl.threadpool.agent;

6
fine-transmittable-thread-local/src/main/java/com/fr/third/alibaba/ttl/threadpool/package-info.java

@ -0,0 +1,6 @@
/**
* Thread pool wrap/decoration utils.
*
* @author Jerry Lee (oldratlee at gmail dot com)
*/
package com.fr.third.alibaba.ttl.threadpool;
Loading…
Cancel
Save