diff --git a/.github/workflows/plugins-test.yaml b/.github/workflows/plugins-test.yaml
index 15b9844eef3fbf06aea35425c0c06c9dab0b6522..9cb28d526441ec5cb536b4293c627976fac79edd 100644
--- a/.github/workflows/plugins-test.yaml
+++ b/.github/workflows/plugins-test.yaml
@@ -446,7 +446,7 @@ jobs:
- name: Run elasticsearch-6.x-scenario 6.7.1-6.8.4 (7)
run: bash test/plugin/run.sh elasticsearch-6.x-scenario
- Oracle_Kafka_JdkHttp:
+ Oracle_Kafka_JdkHttp_JdkThreading:
runs-on: ubuntu-18.04
timeout-minutes: 90
strategy:
@@ -477,6 +477,8 @@ jobs:
run: bash test/plugin/run.sh kafka-scenario
- name: Run jdk http (1)
run: bash test/plugin/run.sh jdk-http-scenario
+ - name: Run jdk threading (1)
+ run: bash test/plugin/run.sh jdk-threading-scenario
MySQL:
runs-on: ubuntu-18.04
diff --git a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java
index 8ac1799458c0890ad0c827d96baa96895898df68..1ec2dec08a42d66e17c8821951873dfb431d2e1a 100755
--- a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java
+++ b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java
@@ -149,4 +149,6 @@ public class ComponentsDefine {
public static final OfficialComponent SPRING_TX = new OfficialComponent(78, "spring-tx");
public static final OfficialComponent ARMERIA = new OfficialComponent(79, "Armeria");
+
+ public static final OfficialComponent JDK_THREADING = new OfficialComponent(80, "JdkThreading");
}
diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java
index 2bc276c3b58edb6d7bc0862fa9574437fbfc169e..0b0842cf9386c3acc466a85b19a2e071cbaca2c3 100755
--- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java
+++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java
@@ -342,5 +342,16 @@ public class Config {
*/
public static boolean SIMPLIFY_TRANSACTION_DEFINITION_NAME = false;
}
+
+ public static class JdkThreading {
+
+ /**
+ * Threading classes ({@link java.lang.Runnable} and {@link java.util.concurrent.Callable}
+ * and their subclasses, including anonymous inner classes)
+ * whose name matches any one of the {@code THREADING_CLASS_PREFIXES} (splitted by ,)
+ * will be instrumented
+ */
+ public static String THREADING_CLASS_PREFIXES = "";
+ }
}
}
diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/HierarchyMatch.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/HierarchyMatch.java
index f134d383a734a5dcd8debc3267e8961f335e39fc..314c4b3cc5aca697c4e7d60fd34040bdf8f657c0 100644
--- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/HierarchyMatch.java
+++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/HierarchyMatch.java
@@ -22,6 +22,7 @@ package org.apache.skywalking.apm.agent.core.plugin.match;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeList;
import net.bytebuddy.matcher.ElementMatcher;
@@ -101,7 +102,7 @@ public class HierarchyMatch implements IndirectMatch {
}
- public static ClassMatch byHierarchyMatch(String[] parentTypes) {
+ public static IndirectMatch byHierarchyMatch(String... parentTypes) {
return new HierarchyMatch(parentTypes);
}
}
diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/PrefixMatch.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/PrefixMatch.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2cb78f65734475f35c7e4bb58dda95971c44d6d
--- /dev/null
+++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/PrefixMatch.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+
+package org.apache.skywalking.apm.agent.core.plugin.match;
+
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import net.bytebuddy.matcher.ElementMatchers;
+
+/**
+ * Match classes by any one of the given {@link #prefixes}
+ *
+ * @author kezhenxu94
+ */
+@SuppressWarnings("rawtypes")
+public class PrefixMatch implements IndirectMatch {
+ private String[] prefixes;
+
+ private PrefixMatch(String... prefixes) {
+ if (prefixes == null || prefixes.length == 0) {
+ throw new IllegalArgumentException("prefixes argument is null or empty");
+ }
+ this.prefixes = prefixes;
+ }
+
+ @Override
+ public ElementMatcher.Junction buildJunction() {
+ ElementMatcher.Junction junction = null;
+
+ for (String prefix : prefixes) {
+ if (junction == null) {
+ junction = ElementMatchers.nameStartsWith(prefix);
+ } else {
+ junction = junction.and(ElementMatchers.nameStartsWith(prefix));
+ }
+ }
+
+ return junction;
+ }
+
+ @Override
+ public boolean isMatch(TypeDescription typeDescription) {
+ for (final String prefix : prefixes) {
+ if (typeDescription.getName().startsWith(prefix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static PrefixMatch nameStartsWith(final String... prefixes) {
+ return new PrefixMatch(prefixes);
+ }
+}
diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/logical/LogicalAndMatch.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/logical/LogicalAndMatch.java
new file mode 100644
index 0000000000000000000000000000000000000000..dc523aebebe1933ef873c3424dc35eccb51d5234
--- /dev/null
+++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/logical/LogicalAndMatch.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.agent.core.plugin.match.logical;
+
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import org.apache.skywalking.apm.agent.core.plugin.match.IndirectMatch;
+
+/**
+ * Match classes by multiple criteria with AND conjunction
+ *
+ * @author kezhenxu94
+ */
+public class LogicalAndMatch implements IndirectMatch {
+ private final IndirectMatch[] indirectMatches;
+
+ /**
+ * Don't instantiate this class directly, use {@link LogicalMatchOperation} instead
+ *
+ * @param indirectMatches the matching criteria to conjunct with AND
+ */
+ LogicalAndMatch(final IndirectMatch... indirectMatches) {
+ this.indirectMatches = indirectMatches;
+ }
+
+ @Override
+ public ElementMatcher.Junction buildJunction() {
+ ElementMatcher.Junction junction = null;
+
+ for (final IndirectMatch indirectMatch : indirectMatches) {
+ if (junction == null) {
+ junction = indirectMatch.buildJunction();
+ } else {
+ junction = junction.and(indirectMatch.buildJunction());
+ }
+ }
+
+ return junction;
+ }
+
+ @Override
+ public boolean isMatch(final TypeDescription typeDescription) {
+ for (final IndirectMatch indirectMatch : indirectMatches) {
+ if (!indirectMatch.isMatch(typeDescription)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+}
diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/logical/LogicalMatchOperation.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/logical/LogicalMatchOperation.java
new file mode 100644
index 0000000000000000000000000000000000000000..0fa5b83f50f328d58dd366c9f8b0df94fda9429c
--- /dev/null
+++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/logical/LogicalMatchOperation.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.agent.core.plugin.match.logical;
+
+import org.apache.skywalking.apm.agent.core.plugin.match.IndirectMatch;
+
+/**
+ * Util class to help to construct logical operations on {@link org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch}s
+ *
+ * @author kezhenxu94
+ */
+public class LogicalMatchOperation {
+ public static IndirectMatch and(final IndirectMatch... matches) {
+ return new LogicalAndMatch(matches);
+ }
+
+ public static IndirectMatch or(final IndirectMatch... matches) {
+ return new LogicalOrMatch(matches);
+ }
+}
diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/logical/LogicalOrMatch.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/logical/LogicalOrMatch.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a10a4fb772f06980223d2d7fd12de0b6a22c679
--- /dev/null
+++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/logical/LogicalOrMatch.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.agent.core.plugin.match.logical;
+
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import org.apache.skywalking.apm.agent.core.plugin.match.IndirectMatch;
+
+/**
+ * Match classes by multiple criteria with OR conjunction
+ *
+ * @author kezhenxu94
+ */
+public class LogicalOrMatch implements IndirectMatch {
+ private final IndirectMatch[] indirectMatches;
+
+ /**
+ * Don't instantiate this class directly, use {@link LogicalMatchOperation} instead
+ *
+ * @param indirectMatches the matching criteria to conjunct with OR
+ */
+ LogicalOrMatch(final IndirectMatch... indirectMatches) {
+ this.indirectMatches = indirectMatches;
+ }
+
+ @Override
+ public ElementMatcher.Junction buildJunction() {
+ ElementMatcher.Junction junction = null;
+
+ for (final IndirectMatch indirectMatch : indirectMatches) {
+ if (junction == null) {
+ junction = indirectMatch.buildJunction();
+ } else {
+ junction = junction.or(indirectMatch.buildJunction());
+ }
+ }
+
+ return junction;
+ }
+
+ @Override
+ public boolean isMatch(final TypeDescription typeDescription) {
+ for (final IndirectMatch indirectMatch : indirectMatches) {
+ if (indirectMatch.isMatch(typeDescription)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/pom.xml b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/pom.xml
new file mode 100755
index 0000000000000000000000000000000000000000..b9d271a1e4f8798e364078c27ee1d0c3ee260be3
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/pom.xml
@@ -0,0 +1,46 @@
+
+
+
+
+ org.apache.skywalking
+ bootstrap-plugins
+ 6.6.0-SNAPSHOT
+
+ 4.0.0
+
+ apm-jdk-threading-plugin
+ jar
+
+ apm-jdk-threading-plugin
+ SkyWalking Java Agent Plugin for JDK threading classes, (Runnable, Callable)
+ https://github.com/apache/skywalking
+
+
+ UTF-8
+
+
+
+
+
+ maven-deploy-plugin
+
+
+
+
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/ThreadingConfig.java b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/ThreadingConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..97145a6c72c125d56cffe1f9aa0efcc063c96686
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/ThreadingConfig.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.jdk.threading;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.skywalking.apm.agent.core.conf.Config;
+import org.apache.skywalking.apm.agent.core.logging.api.ILog;
+import org.apache.skywalking.apm.agent.core.logging.api.LogManager;
+import org.apache.skywalking.apm.agent.core.plugin.match.IndirectMatch;
+import org.apache.skywalking.apm.agent.core.plugin.match.logical.LogicalMatchOperation;
+import org.apache.skywalking.apm.agent.core.plugin.match.PrefixMatch;
+
+import static org.apache.skywalking.apm.agent.core.plugin.match.PrefixMatch.nameStartsWith;
+
+/**
+ * @author kezhenxu94
+ */
+public class ThreadingConfig {
+ private static final ILog LOGGER = LogManager.getLogger(ThreadingConfig.class);
+
+ public static IndirectMatch prefixesMatchesForJdkThreading() {
+ final String jointPrefixes = Config.Plugin.JdkThreading.THREADING_CLASS_PREFIXES;
+
+ if (jointPrefixes == null || jointPrefixes.trim().isEmpty()) {
+ return null;
+ }
+
+ final String[] prefixes = jointPrefixes.split(",");
+
+ final List prefixMatches = new ArrayList();
+
+ for (final String prefix : prefixes) {
+ if (prefix.startsWith("java.") || prefix.startsWith("javax.")) {
+ LOGGER.warn("prefix {} is ignored", prefix);
+ continue;
+ }
+ prefixMatches.add(nameStartsWith(prefix));
+ }
+
+ if (prefixMatches.size() == 0) {
+ return null;
+ }
+
+ return LogicalMatchOperation.or(prefixMatches.toArray(new PrefixMatch[0]));
+ }
+}
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/ThreadingConstructorInterceptor.java b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/ThreadingConstructorInterceptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ca000e6c4dec8ebd887df214fa860fc14017e93
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/ThreadingConstructorInterceptor.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.jdk.threading;
+
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
+
+/**
+ * @author kezhenxu94
+ */
+public class ThreadingConstructorInterceptor implements InstanceConstructorInterceptor {
+
+ @Override
+ public void onConstruct(final EnhancedInstance objInst, final Object[] allArguments) {
+ if (ContextManager.isActive()) {
+ objInst.setSkyWalkingDynamicField(ContextManager.capture());
+ }
+ }
+
+}
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/ThreadingMethodInterceptor.java b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/ThreadingMethodInterceptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1607daac0eb68bf6b2dcf4315d12794b4148e95
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/ThreadingMethodInterceptor.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.jdk.threading;
+
+import java.lang.reflect.Method;
+
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
+
+/**
+ * @author kezhenxu94
+ */
+public class ThreadingMethodInterceptor implements InstanceMethodsAroundInterceptor {
+
+ @Override
+ public void beforeMethod(
+ final EnhancedInstance objInst,
+ final Method method,
+ final Object[] allArguments,
+ final Class>[] argumentsTypes,
+ final MethodInterceptResult result) {
+
+ AbstractSpan span = ContextManager.createLocalSpan(generateOperationName(objInst, method));
+ span.setComponent(ComponentsDefine.JDK_THREADING);
+
+ final Object storedField = objInst.getSkyWalkingDynamicField();
+ if (storedField != null) {
+ final ContextSnapshot contextSnapshot = (ContextSnapshot) storedField;
+ ContextManager.continued(contextSnapshot);
+ }
+
+ }
+
+ @Override
+ public Object afterMethod(
+ final EnhancedInstance objInst,
+ final Method method,
+ final Object[] allArguments,
+ final Class>[] argumentsTypes,
+ final Object ret) {
+
+ final Object storedField = objInst.getSkyWalkingDynamicField();
+ if (storedField != null) {
+ ContextManager.stopSpan();
+ }
+
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(
+ final EnhancedInstance objInst,
+ final Method method,
+ final Object[] allArguments,
+ final Class>[] argumentsTypes,
+ final Throwable t) {
+
+ if (ContextManager.isActive()) {
+ ContextManager.activeSpan().errorOccurred().log(t);
+ }
+ }
+
+ private String generateOperationName(final EnhancedInstance objInst, final Method method) {
+ return "Threading/" + objInst.getClass().getName() + "/" + method.getName();
+ }
+
+}
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/define/CallableInstrumentation.java b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/define/CallableInstrumentation.java
new file mode 100644
index 0000000000000000000000000000000000000000..a32560e8a2094b8207770f8142246902e3c37d47
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/define/CallableInstrumentation.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.jdk.threading.define;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassEnhancePluginDefine;
+import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
+import org.apache.skywalking.apm.agent.core.plugin.match.IndirectMatch;
+import org.apache.skywalking.apm.agent.core.plugin.match.logical.LogicalMatchOperation;
+import org.apache.skywalking.apm.plugin.jdk.threading.ThreadingConfig;
+
+import static net.bytebuddy.matcher.ElementMatchers.any;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
+import static org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch.byHierarchyMatch;
+
+/**
+ * @author kezhenxu94
+ */
+public class CallableInstrumentation extends ClassEnhancePluginDefine {
+ private static final String CALLABLE_CLASS = "java.util.concurrent.Callable";
+ private static final String CALLABLE_CLASS_INTERCEPTOR = "org.apache.skywalking.apm.plugin.jdk.threading.ThreadingConstructorInterceptor";
+
+ private static final String CALLABLE_CALL_METHOD = "call";
+ private static final String CALLABLE_CALL_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.plugin.jdk.threading.ThreadingMethodInterceptor";
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ final IndirectMatch prefixMatches = ThreadingConfig.prefixesMatchesForJdkThreading();
+
+ if (prefixMatches == null) {
+ return null;
+ }
+
+ return LogicalMatchOperation.and(prefixMatches, byHierarchyMatch(CALLABLE_CLASS));
+ }
+
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[]{
+ new ConstructorInterceptPoint() {
+ @Override
+ public ElementMatcher getConstructorMatcher() {
+ return any();
+ }
+
+ @Override
+ public String getConstructorInterceptor() {
+ return CALLABLE_CLASS_INTERCEPTOR;
+ }
+ }
+ };
+ }
+
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[]{
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named(CALLABLE_CALL_METHOD).and(takesArguments(0));
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return CALLABLE_CALL_METHOD_INTERCEPTOR;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return false;
+ }
+ }
+ };
+ }
+
+ @Override
+ public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
+ return new StaticMethodsInterceptPoint[0];
+ }
+
+ @Override
+ public boolean isBootstrapInstrumentation() {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/define/RunnableInstrumentation.java b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/define/RunnableInstrumentation.java
new file mode 100644
index 0000000000000000000000000000000000000000..c77e256d55a95d354c32373dda87abc06257ef1b
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/java/org/apache/skywalking/apm/plugin/jdk/threading/define/RunnableInstrumentation.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.jdk.threading.define;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassEnhancePluginDefine;
+import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
+import org.apache.skywalking.apm.agent.core.plugin.match.IndirectMatch;
+import org.apache.skywalking.apm.agent.core.plugin.match.logical.LogicalMatchOperation;
+import org.apache.skywalking.apm.plugin.jdk.threading.ThreadingConfig;
+
+import static net.bytebuddy.matcher.ElementMatchers.any;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
+import static org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch.byHierarchyMatch;
+
+/**
+ * @author kezhenxu94
+ */
+public class RunnableInstrumentation extends ClassEnhancePluginDefine {
+ private static final String RUNNABLE_CLASS = "java.lang.Runnable";
+ private static final String RUNNABLE_CLASS_INTERCEPTOR = "org.apache.skywalking.apm.plugin.jdk.threading.ThreadingConstructorInterceptor";
+
+ private static final String RUNNABLE_RUN_METHOD = "run";
+ private static final String RUNNABLE_RUN_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.plugin.jdk.threading.ThreadingMethodInterceptor";
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ final IndirectMatch prefixMatches = ThreadingConfig.prefixesMatchesForJdkThreading();
+
+ if (prefixMatches == null) {
+ return null;
+ }
+
+ return LogicalMatchOperation.and(prefixMatches, byHierarchyMatch(RUNNABLE_CLASS));
+ }
+
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[]{
+ new ConstructorInterceptPoint() {
+ @Override
+ public ElementMatcher getConstructorMatcher() {
+ return any();
+ }
+
+ @Override
+ public String getConstructorInterceptor() {
+ return RUNNABLE_CLASS_INTERCEPTOR;
+ }
+ }
+ };
+ }
+
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[]{
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named(RUNNABLE_RUN_METHOD).and(takesArguments(0));
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return RUNNABLE_RUN_METHOD_INTERCEPTOR;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return false;
+ }
+ }
+ };
+ }
+
+ @Override
+ public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
+ return new StaticMethodsInterceptPoint[0];
+ }
+
+ @Override
+ public boolean isBootstrapInstrumentation() {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/resources/skywalking-plugin.def
new file mode 100644
index 0000000000000000000000000000000000000000..06aaee35e1e23c5fde8461b23e40804219084def
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threading-plugin/src/main/resources/skywalking-plugin.def
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jdk-threading-plugin=org.apache.skywalking.apm.plugin.jdk.threading.define.RunnableInstrumentation
+jdk-threading-plugin=org.apache.skywalking.apm.plugin.jdk.threading.define.CallableInstrumentation
\ No newline at end of file
diff --git a/apm-sniffer/bootstrap-plugins/pom.xml b/apm-sniffer/bootstrap-plugins/pom.xml
index b421ee1c1faa26fe44653bee5fd72b46dead6e31..eef458376e07ea8f76e5270bffc4eae003dbaa85 100644
--- a/apm-sniffer/bootstrap-plugins/pom.xml
+++ b/apm-sniffer/bootstrap-plugins/pom.xml
@@ -42,6 +42,7 @@
jdk-http-plugin
+ jdk-threading-plugin
diff --git a/apm-sniffer/config/agent.config b/apm-sniffer/config/agent.config
index 6e03ff2807759c091625989986aa509ce293c051..dfa6aeee9e9d1c1cc193cbca7cae96b38ae45850 100644
--- a/apm-sniffer/config/agent.config
+++ b/apm-sniffer/config/agent.config
@@ -28,14 +28,14 @@ agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
# agent.authentication = ${SW_AGENT_AUTHENTICATION:xxxx}
# The max amount of spans in a single segment.
-# Through this config item, skywalking keep your application memory cost estimated.
+# Through this config item, SkyWalking keep your application memory cost estimated.
# agent.span_limit_per_segment=${SW_AGENT_SPAN_LIMIT:300}
# Ignore the segments if their operation names end with these suffix.
# agent.ignore_suffix=${SW_AGENT_IGNORE_SUFFIX:.jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg}
-# If true, skywalking agent will save all instrumented classes files in `/debugging` folder.
-# Skywalking team may ask for these files in order to resolve compatible problem.
+# If true, SkyWalking agent will save all instrumented classes files in `/debugging` folder.
+# SkyWalking team may ask for these files in order to resolve compatible problem.
# agent.is_open_debugging_class = ${SW_AGENT_OPEN_DEBUG:true}
# The operationName max length
@@ -61,4 +61,4 @@ logging.level=${SW_LOGGING_LEVEL:DEBUG}
# logging.max_history_files=${SW_LOGGING_MAX_HISTORY_FILES:-1}
# mysql plugin configuration
-# plugin.mysql.trace_sql_parameters=${SW_MYSQL_TRACE_SQL_PARAMETERS:false}
\ No newline at end of file
+# plugin.mysql.trace_sql_parameters=${SW_MYSQL_TRACE_SQL_PARAMETERS:false}
diff --git a/docs/en/setup/service-agent/java-agent/README.md b/docs/en/setup/service-agent/java-agent/README.md
index 787d90ea3f99190f25948203e9d40c766e43fc16..c316651ba5650898859610c1de72df873f8ae8a9 100755
--- a/docs/en/setup/service-agent/java-agent/README.md
+++ b/docs/en/setup/service-agent/java-agent/README.md
@@ -117,6 +117,7 @@ property key | Description | Default |
`plugin.light4j.trace_handler_chain`|If true, trace all middleware/business handlers that are part of the Light4J handler chain for a request.|false|
`plugin.opgroup.*`|Support operation name customize group rules in different plugins. Read [Group rule supported plugins](op_name_group_rule.md)|Not set|
`plugin.springtransaction.simplify_transaction_definition_name`|If true, the transaction definition name will be simplified.|false|
+`plugin.jdkthreading.threading_class_prefixes` | Threading classes (`java.lang.Runnable` and `java.util.concurrent.Callable`) and their subclasses, including anonymous inner classes whose name match any one of the `THREADING_CLASS_PREFIXES` (splitted by `,`) will be instrumented, make sure to only specify as narrow prefixes as what you're expecting to instrument, (`java.` and `javax.` will be ignored due to safety issues) | Not set |
## Optional Plugins
Java agent plugins are all pluggable. Optional plugins could be provided in `optional-plugins` folder under agent or 3rd party repositories.
@@ -140,6 +141,7 @@ For using these plugins, you need to put the target plugin jar file into `/plugi
Now, we have the following known bootstrap plugins.
* Plugin of JDK HttpURLConnection. Agent is compatible with JDK 1.6+
+* Plugin of JDK Callable and Runnable. Agent is compatible with JDK 1.6+
## Advanced Features
* Set the settings through system properties for config file override. Read [setting override](Setting-override.md).
diff --git a/oap-server/server-bootstrap/src/main/resources/component-libraries.yml b/oap-server/server-bootstrap/src/main/resources/component-libraries.yml
index 90e1d65980224d770d36ac43ac02de239c2188b7..bac1b0ce19d2209d33a69ce0f64f04ec01e57b62 100755
--- a/oap-server/server-bootstrap/src/main/resources/component-libraries.yml
+++ b/oap-server/server-bootstrap/src/main/resources/component-libraries.yml
@@ -266,6 +266,9 @@ spring-tx:
Armeria:
id: 79
languages: Java
+JdkThreading:
+ id: 80
+ languages: Java
# .NET/.NET Core components
# [3000, 4000) for C#/.NET only
diff --git a/test/plugin/scenarios/jdk-threading-scenario/bin/startup.sh b/test/plugin/scenarios/jdk-threading-scenario/bin/startup.sh
new file mode 100644
index 0000000000000000000000000000000000000000..98bfae7053c9860a8687d86d124fec5f95b598ff
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threading-scenario/bin/startup.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+home="$(cd "$(dirname $0)"; pwd)"
+
+java -Dskywalking.plugin.jdkthreading.threading_class_prefixes=test.org.apache.skywalking. -jar ${agent_opts} ${home}/../libs/jdk-threading-scenario.jar &
\ No newline at end of file
diff --git a/test/plugin/scenarios/jdk-threading-scenario/config/expectedData.yaml b/test/plugin/scenarios/jdk-threading-scenario/config/expectedData.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..c4c492d3d7c8ef943bd83083d389af3012de484c
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threading-scenario/config/expectedData.yaml
@@ -0,0 +1,121 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+registryItems:
+ applications:
+ - {jdk-threading-scenario: 2}
+ instances:
+ - {jdk-threading-scenario: 1}
+ operationNames:
+ - jdk-threading-scenario: ['/greet/{username}']
+ heartbeat: []
+
+segmentItems:
+ - applicationCode: jdk-threading-scenario
+ segmentSize: ge 4
+ segments:
+ - segmentId: not null
+ spans:
+ - operationName: /greet/{username}
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 14
+ componentName: ''
+ isError: false
+ spanType: Entry
+ peer: ''
+ peerId: 0
+ tags:
+ - {key: url, value: 'http://localhost:8080/greet/skywalking'}
+ - {key: http.method, value: GET}
+
+ - segmentId: not null
+ spans:
+ - operationName: /apache/skywalking
+ operationId: 0
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 13
+ componentName: ''
+ isError: false
+ spanType: Exit
+ peer: github.com:443
+ peerId: 0
+ tags:
+ - {key: url, value: 'https://github.com:-1/apache/skywalking'}
+ - {key: http.method, value: GET}
+ - operationName: Threading/test.org.apache.skywalking.apm.testcase.jdk.threading.Application$TestController$1/run
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Unknown
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 80
+ componentName: ''
+ isError: false
+ spanType: Local
+ peer: ''
+ peerId: 0
+ refs:
+ - {parentEndpointId: 0, parentEndpoint: '/greet/{username}', networkAddressId: 0,
+ entryEndpointId: 0, refType: CrossThread, parentSpanId: 0, parentTraceSegmentId: not null,
+ parentServiceInstanceId: 1, networkAddress: '', entryEndpoint: '/greet/{username}',
+ entryServiceInstanceId: 1}
+
+ - segmentId: not null
+ spans:
+ - operationName: /apache/skywalking
+ operationId: 0
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 13
+ componentName: ''
+ isError: false
+ spanType: Exit
+ peer: github.com:443
+ peerId: 0
+ tags:
+ - {key: url, value: 'https://github.com:-1/apache/skywalking'}
+ - {key: http.method, value: GET}
+ - operationName: Threading/test.org.apache.skywalking.apm.testcase.jdk.threading.Application$TestController$2/call
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Unknown
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 80
+ componentName: ''
+ isError: false
+ spanType: Local
+ peer: ''
+ peerId: 0
+ refs:
+ - {parentEndpointId: 0, parentEndpoint: '/greet/{username}', networkAddressId: 0,
+ entryEndpointId: 0, refType: CrossThread, parentSpanId: 0, parentTraceSegmentId: not null,
+ parentServiceInstanceId: 1, networkAddress: '', entryEndpoint: '/greet/{username}',
+ entryServiceInstanceId: 1}
\ No newline at end of file
diff --git a/test/plugin/scenarios/jdk-threading-scenario/configuration.yml b/test/plugin/scenarios/jdk-threading-scenario/configuration.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b129cc2a1ff1010ae1e061a40555fb780656b904
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threading-scenario/configuration.yml
@@ -0,0 +1,23 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+type: jvm
+entryService: http://localhost:8080/greet/skywalking
+healthCheck: http://localhost:8080/healthCheck
+runningMode: with_bootstrap
+withPlugins: apm-jdk-threading-plugin-*.jar
+startScript: ./bin/startup.sh
+framework: jdk-threading-scenario
diff --git a/test/plugin/scenarios/jdk-threading-scenario/pom.xml b/test/plugin/scenarios/jdk-threading-scenario/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..42b68ac9b88492d58251f03dc1873577e6334214
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threading-scenario/pom.xml
@@ -0,0 +1,88 @@
+
+
+
+ 4.0.0
+
+ org.apache.skywalking
+ jdk-threading-scenario
+ 5.0.0
+
+
+ UTF-8
+ 1.6
+ 2.1.6.RELEASE
+
+
+ skywalking-jdk-threading-scenario
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring.boot.version}
+
+
+
+
+ jdk-threading-scenario
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+
+
+ repackage
+
+
+
+
+
+ maven-compiler-plugin
+ 3.8.1
+
+
+ ${compiler.version}
+ ${project.build.sourceEncoding}
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ assemble
+ package
+
+ single
+
+
+
+ src/main/assembly/assembly.xml
+
+ ./target/
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/plugin/scenarios/jdk-threading-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/jdk-threading-scenario/src/main/assembly/assembly.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a73328614cdf987477515c98de6930b6dbb0661d
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threading-scenario/src/main/assembly/assembly.xml
@@ -0,0 +1,41 @@
+
+
+
+
+ zip
+
+
+
+
+ ./bin
+ 0775
+
+
+
+
+
+
+ ./libs
+ 0775
+
+
+
\ No newline at end of file
diff --git a/test/plugin/scenarios/jdk-threading-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/jdk/threading/Application.java b/test/plugin/scenarios/jdk-threading-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/jdk/threading/Application.java
new file mode 100644
index 0000000000000000000000000000000000000000..1cbdedbb545d084e60a78e8a09bd5df6c11f6a6c
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threading-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/jdk/threading/Application.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package test.org.apache.skywalking.apm.testcase.jdk.threading;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author kezhenxu94
+ */
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+ @Bean
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+
+ @RestController
+ static class TestController {
+ private final RestTemplate restTemplate;
+ private final ExecutorService executorService;
+
+ public TestController(final RestTemplate restTemplate) {
+ this.restTemplate = restTemplate;
+ this.executorService = Executors.newSingleThreadScheduledExecutor();
+ }
+
+ @GetMapping("/healthCheck")
+ public String healthCheck() {
+ return "Success";
+ }
+
+ @GetMapping("/greet/{username}")
+ public String testCase(@PathVariable final String username) throws ExecutionException, InterruptedException {
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ restTemplate.getForEntity("https://github.com/apache/skywalking", String.class);
+ }
+ };
+
+ executorService.execute(runnable);
+
+ executorService.submit(new Callable() {
+ @Override
+ public String call() {
+ return restTemplate.getForEntity("https://github.com/apache/skywalking", String.class).getBody();
+ }
+ }).get();
+
+ return username;
+ }
+ }
+}
diff --git a/test/plugin/scenarios/jdk-threading-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/jdk-threading-scenario/src/main/resources/application.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6ff39eb8eafc6f543aaac7dd8d7992d71041fea8
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threading-scenario/src/main/resources/application.yaml
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+server:
+ port: 8080
+
diff --git a/test/plugin/scenarios/jdk-threading-scenario/support-version.list b/test/plugin/scenarios/jdk-threading-scenario/support-version.list
new file mode 100644
index 0000000000000000000000000000000000000000..feef03cdea9d00b52f68d88fc4bd5773cc072290
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threading-scenario/support-version.list
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+all
\ No newline at end of file