diff --git a/.travis.yml b/.travis.yml
index 1a2df5ca2885ede4d341697929771c6bbcea3ee1..5bbd8762e807e33a9a46a2b82c98c35522992c6e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -40,7 +40,8 @@ before_install:
- echo -e "JAVA_HOME=$JAVA_HOME\nPATH=$PATH\nSHELL=$SHELL"
script:
- - ./scripts/integration-test.sh
+ - scripts/integration-test.sh
+ - ttl-integrations/sample-ttl-agent-extension-transformlet/scripts/integration-test.sh
after_success:
# codecov
diff --git a/ttl-integrations/sample-ttl-agent-extension-transformlet/README.md b/ttl-integrations/sample-ttl-agent-extension-transformlet/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..f4a179d5d5eebe102a08ee576db9edba2b3a50dc
--- /dev/null
+++ b/ttl-integrations/sample-ttl-agent-extension-transformlet/README.md
@@ -0,0 +1,45 @@
+# `TTL Agent`扩展`Transformlet`实现的示例工程
+
+## 扩展`Transformlet`的实现
+
+为了提供`TTL Agent`扩展`Transformlet`,包含2部分:
+
+1. `TTL Agent`扩展`Transformlet`的实现类:[`SampleExtensionTransformlet`](src/main/java/com/alibaba/ttl/agent/extension_transformlet/sample/transformlet/SampleExtensionTransformlet.java)。
+ - 这个示例`Transformlet`修改了类[`ToBeTransformedClass`](src/main/java/com/alibaba/ttl/agent/extension_transformlet/sample/biz/ToBeTransformedClass.java)的`toBeTransformedMethod`方法:在修改方法前插入一行代码,修改方法参数值乘以2(`$1 *= 2;`)。
+1. `TTL Agent`扩展`Transformlet`的配置文件:[`META-INF/ttl.agent.transformlets`](src/main/resources/META-INF/ttl.agent.transformlets)
+ - 配置文件的内容是 扩展`Transformlet`实现类的全类名。
+ 在这个示例工程是`com.alibaba.ttl.agent.extension_transformlet.sample.transformlet.SampleExtensionTransformlet`。
+ - `TTL Agent`会扫描`Class Path`上的`META-INF/ttl.agent.transformlets`文件,自动发现并启用这些扩展`Transformlet`。
+ 即只要将扩展`Transformlet`的依赖`Jar`引入到应用中就会自动生效。
+ - 这个扫描并自动加载生效与`JDK`的[`ServiceLoader`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.html)一样,只是使用不同的扩展配置文件。
+
+## 扩展`Transformlet`的测试与生效验证
+
+单元测试类 在 [`ToBeTransformedClassTest`](src/test/java/com/alibaba/ttl/agent/extension_transformlet/sample/biz/ToBeTransformedClassTest.java)。
+
+通过运行`Maven`单元测试验证扩展`Transformlet` `SampleExtensionTransformlet`是否生效:
+
+
+```bash
+# sample-ttl-agent-extension-transformlet 工程目录,执行
+
+# 1. 先 mvn install TTL lib
+(cd ../.. && mvn install -Dmaven.test.skip)
+
+# 2. 验证 扩展Transformlet SampleExtensionTransformlet 是否生效
+mvn test -Penable-TtlAgent-forTest
+# 更多输出TTL的Transform类操作的日志
+mvn test -Penable-TtlAgent-forTest -Penable-LogTransform-forTest
+```
+
+## 运行示例`SampleMain`
+
+可以通过`Java`命令行参数来运行示例`SampleMain`:
+
+```java
+java -javaagent:path/to/transmittable-thread-local-2.x.y.jar \
+ -cp target/classes \
+ com.alibaba.ttl.agent.extension_transformlet.sample.biz.SampleMain
+```
+
+通过脚本[`scripts/run.sh`](scripts/run.sh)快速上面命令行的运行。
diff --git a/ttl-integrations/sample-ttl-agent-extension-transformlet/pom.xml b/ttl-integrations/sample-ttl-agent-extension-transformlet/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f81bfa7eb3fd954b6001fb9ac1744c4fb83679a0
--- /dev/null
+++ b/ttl-integrations/sample-ttl-agent-extension-transformlet/pom.xml
@@ -0,0 +1,109 @@
+
+ 4.0.0
+
+ com.alibaba
+ sample-ttl-agent-extension-transformlet
+ 1.0.0-SNAPSHOT
+ jar
+ ${project.artifactId}
+
+
+ 1.8
+ 1.8
+ UTF-8
+
+ 2.13.0-SNAPSHOT
+
+
+
+
+ com.alibaba
+ transmittable-thread-local
+ ${ttl.version}
+
+
+ junit
+ junit
+ 4.13.1
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.2
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
+
+
+ enable-TtlAgent-forTest
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+
+ ${surefire.verbose.class}
+ -javaagent:${com.alibaba:transmittable-thread-local:jar}=ttl.agent.logger:STDOUT
+ ${surefire.ttl.agent.log.class.transform}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.1.2
+
+
+ initialize
+
+ properties
+
+
+
+
+
+
+
+
+ enable-LogTransform-forTest
+
+ -Dttl.agent.log.class.transform
+
+
+
+ enable-verboseClass-forTest
+
+ -verbose:class
+
+
+
+
diff --git a/ttl-integrations/sample-ttl-agent-extension-transformlet/scripts/integration-test.sh b/ttl-integrations/sample-ttl-agent-extension-transformlet/scripts/integration-test.sh
new file mode 100755
index 0000000000000000000000000000000000000000..b17cb32be7f488cae6a61cefeb8443f848205de5
--- /dev/null
+++ b/ttl-integrations/sample-ttl-agent-extension-transformlet/scripts/integration-test.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+set -eEuo pipefail
+# adjust current dir to project dir
+cd "$(dirname "$(readlink -f "$0")")/.."
+
+TTL_ROOT_PROJECT_DIR="$(dirname "$(readlink -f "../")")"
+
+source "$TTL_ROOT_PROJECT_DIR/scripts/common_build.sh"
+source "$TTL_ROOT_PROJECT_DIR/scripts/prepare-jdk.sh"
+
+for jv in 8 11; do
+ switch_to_jdk "$jv"
+
+ headInfo "test with JDK $JAVA_HOME"
+
+ MVN_WITH_BASIC_OPTIONS test
+ MVN_WITH_BASIC_OPTIONS test -Penable-TtlAgent-forTest
+done
diff --git a/ttl-integrations/sample-ttl-agent-extension-transformlet/scripts/run.sh b/ttl-integrations/sample-ttl-agent-extension-transformlet/scripts/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6e35a5e80f4a9bde5c3321d4effa2847a1a80ba6
--- /dev/null
+++ b/ttl-integrations/sample-ttl-agent-extension-transformlet/scripts/run.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+set -eEuo pipefail
+# adjust current dir to project dir
+cd "$(dirname "$(readlink -f "$0")")/.."
+
+TTL_ROOT_PROJECT_DIR="$(dirname "$(readlink -f "../")")"
+
+source "$TTL_ROOT_PROJECT_DIR/scripts/common_build.sh"
+
+ttl_version=$(extractFirstElementValueFromPom version "../../pom.xml")
+
+readonly ttl_agent_path="$TTL_ROOT_PROJECT_DIR/target/transmittable-thread-local-$ttl_version.jar"
+
+mvn_ttl_lib() {
+ (
+ cd "$TTL_ROOT_PROJECT_DIR"
+ MVN_WITH_BASIC_OPTIONS -q -Dmaven.test.skip "$@"
+ )
+}
+
+if [ "${1:-}" != "skipClean" ]; then
+ mvn_ttl_lib clean package
+
+ # compile sample-ttl-agent-extension-transformlet
+ MVN_WITH_BASIC_OPTIONS -q clean compile
+else
+ if [ ! -f "$ttl_agent_path" ]; then
+ mvn_ttl_lib package
+ fi
+
+ # compile sample-ttl-agent-extension-transformlet
+ MVN_WITH_BASIC_OPTIONS -q compile
+fi
+
+readonly ttl_agent_options="-javaagent:$ttl_agent_path=ttl.agent.logger:STDOUT,ttl.agent.log.class.transform:true"
+
+readonly main_class=com.alibaba.ttl.agent.extension_transformlet.sample.biz.SampleMain
+
+logAndRun "$JAVA_HOME/bin/java" -Duser.language=en -Duser.country=US \
+ "${ttl_agent_options}" \
+ -cp target/classes $main_class
diff --git a/ttl-integrations/sample-ttl-agent-extension-transformlet/src/main/java/com/alibaba/ttl/agent/extension_transformlet/sample/biz/SampleMain.java b/ttl-integrations/sample-ttl-agent-extension-transformlet/src/main/java/com/alibaba/ttl/agent/extension_transformlet/sample/biz/SampleMain.java
new file mode 100644
index 0000000000000000000000000000000000000000..3010f490798a5ba9e991b63496252be82aba1272
--- /dev/null
+++ b/ttl-integrations/sample-ttl-agent-extension-transformlet/src/main/java/com/alibaba/ttl/agent/extension_transformlet/sample/biz/SampleMain.java
@@ -0,0 +1,23 @@
+package com.alibaba.ttl.agent.extension_transformlet.sample.biz;
+
+import com.alibaba.ttl.threadpool.agent.TtlAgent;
+import com.alibaba.ttl.threadpool.agent.transformlet.ClassInfo;
+
+public class SampleMain {
+ /**
+ * @see ToBeTransformedClass#toBeTransformedMethod(int)
+ * @see com.alibaba.ttl.agent.extension_transformlet.sample.transformlet.SampleExtensionTransformlet#doTransform(ClassInfo)
+ */
+ public static void main(String[] args) throws Exception {
+ final ToBeTransformedClass instance = new ToBeTransformedClass();
+
+ System.out.println("========================================");
+ if (TtlAgent.isTtlAgentLoaded()) {
+ System.out.println("Run WITH TTL Agent");
+ } else {
+ System.out.println("Run Without TTL Agent");
+ }
+ System.out.println(instance.toBeTransformedMethod(21));
+ System.out.println("========================================");
+ }
+}
diff --git a/ttl-integrations/sample-ttl-agent-extension-transformlet/src/main/java/com/alibaba/ttl/agent/extension_transformlet/sample/biz/ToBeTransformedClass.java b/ttl-integrations/sample-ttl-agent-extension-transformlet/src/main/java/com/alibaba/ttl/agent/extension_transformlet/sample/biz/ToBeTransformedClass.java
new file mode 100644
index 0000000000000000000000000000000000000000..bdb206dbfe10cf97193e069fa6b6deb1c8daec09
--- /dev/null
+++ b/ttl-integrations/sample-ttl-agent-extension-transformlet/src/main/java/com/alibaba/ttl/agent/extension_transformlet/sample/biz/ToBeTransformedClass.java
@@ -0,0 +1,7 @@
+package com.alibaba.ttl.agent.extension_transformlet.sample.biz;
+
+public class ToBeTransformedClass {
+ public int toBeTransformedMethod(int input) {
+ return input;
+ }
+}
diff --git a/ttl-integrations/sample-ttl-agent-extension-transformlet/src/main/java/com/alibaba/ttl/agent/extension_transformlet/sample/transformlet/SampleExtensionTransformlet.java b/ttl-integrations/sample-ttl-agent-extension-transformlet/src/main/java/com/alibaba/ttl/agent/extension_transformlet/sample/transformlet/SampleExtensionTransformlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..e64d6d26919279ecc9b88f026d6b9f82e4679957
--- /dev/null
+++ b/ttl-integrations/sample-ttl-agent-extension-transformlet/src/main/java/com/alibaba/ttl/agent/extension_transformlet/sample/transformlet/SampleExtensionTransformlet.java
@@ -0,0 +1,43 @@
+package com.alibaba.ttl.agent.extension_transformlet.sample.transformlet;
+
+import com.alibaba.ttl.agent.extension_transformlet.sample.biz.ToBeTransformedClass;
+import com.alibaba.ttl.threadpool.agent.logging.Logger;
+import com.alibaba.ttl.threadpool.agent.transformlet.ClassInfo;
+import com.alibaba.ttl.threadpool.agent.transformlet.TtlTransformlet;
+import com.alibaba.ttl.threadpool.agent.transformlet.javassist.CannotCompileException;
+import com.alibaba.ttl.threadpool.agent.transformlet.javassist.CtClass;
+import com.alibaba.ttl.threadpool.agent.transformlet.javassist.CtMethod;
+import com.alibaba.ttl.threadpool.agent.transformlet.javassist.NotFoundException;
+
+import java.io.IOException;
+
+/**
+ * {@link TtlTransformlet} for {@link ToBeTransformedClass}.
+ *
+ * Caution:
+ * MUST use string constant for class/method name!
+ *
+ * MUST NOT use {@code Class> class = ToBeTransformedClass.class} to get the class to be transformed({@code ToBeTransformedClass}),
+ * {@code ToBeTransformedClass.class} operation will force to load the class to be transformed,
+ * and cause the Transformlet to SKIP the class transform!
+ */
+public class SampleExtensionTransformlet implements TtlTransformlet {
+ private static final Logger logger = Logger.getLogger(SampleExtensionTransformlet.class);
+
+ public static final String TO_BE_TRANSFORMED_CLASS_NAME = "com.alibaba.ttl.agent.extension_transformlet.sample.biz.ToBeTransformedClass";
+ public static final String TO_BE_TRANSFORMED_METHOD = "toBeTransformedMethod";
+
+ public void doTransform(ClassInfo classInfo) throws IOException, NotFoundException, CannotCompileException {
+ if (!classInfo.getClassName().equals(TO_BE_TRANSFORMED_CLASS_NAME)) return;
+
+ final CtClass ctClass = classInfo.getCtClass();
+ final CtMethod method = ctClass.getDeclaredMethod(TO_BE_TRANSFORMED_METHOD);
+
+ final String code = "$1 *= 2;";
+ method.insertBefore(code);
+ logger.info("[SampleExtensionTransformlet] insert code before method " + TO_BE_TRANSFORMED_METHOD
+ + " of class " + method.getDeclaringClass().getName() + ": " + code);
+
+ classInfo.setModified();
+ }
+}
diff --git a/ttl-integrations/sample-ttl-agent-extension-transformlet/src/main/resources/META-INF/ttl.agent.transformlets b/ttl-integrations/sample-ttl-agent-extension-transformlet/src/main/resources/META-INF/ttl.agent.transformlets
new file mode 100644
index 0000000000000000000000000000000000000000..e93147c53deddb7760365d49d9c4da7e2169ef1d
--- /dev/null
+++ b/ttl-integrations/sample-ttl-agent-extension-transformlet/src/main/resources/META-INF/ttl.agent.transformlets
@@ -0,0 +1 @@
+com.alibaba.ttl.agent.extension_transformlet.sample.transformlet.SampleExtensionTransformlet
diff --git a/ttl-integrations/sample-ttl-agent-extension-transformlet/src/test/java/com/alibaba/ttl/agent/extension_transformlet/sample/biz/ToBeTransformedClassTest.java b/ttl-integrations/sample-ttl-agent-extension-transformlet/src/test/java/com/alibaba/ttl/agent/extension_transformlet/sample/biz/ToBeTransformedClassTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..16143cf80d5db42350204184ef61c05364278c64
--- /dev/null
+++ b/ttl-integrations/sample-ttl-agent-extension-transformlet/src/test/java/com/alibaba/ttl/agent/extension_transformlet/sample/biz/ToBeTransformedClassTest.java
@@ -0,0 +1,25 @@
+package com.alibaba.ttl.agent.extension_transformlet.sample.biz;
+
+import com.alibaba.ttl.threadpool.agent.TtlAgent;
+import org.junit.Test;
+
+import java.util.Properties;
+
+import static org.junit.Assert.assertEquals;
+
+public class ToBeTransformedClassTest {
+ @Test
+ public void test_method1() {
+ final ToBeTransformedClass instance = new ToBeTransformedClass();
+
+ System.out.println("========================================");
+ if (TtlAgent.isTtlAgentLoaded()) {
+ System.out.println("Test **WITH** TTL Agent");
+ assertEquals(42, instance.toBeTransformedMethod(21));
+ } else {
+ System.out.println("Test WITHOUT TTL Agent");
+ assertEquals(21, instance.toBeTransformedMethod(21));
+ }
+ System.out.println("========================================");
+ }
+}