You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2022/04/12 12:12:59 UTC
[skywalking-java] branch main updated: [Feature] Add JDK ThreadPoolExecutor plugin(#8743) (#146)
This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-java.git
The following commit(s) were added to refs/heads/main by this push:
new a5ccbca69 [Feature] Add JDK ThreadPoolExecutor plugin(#8743) (#146)
a5ccbca69 is described below
commit a5ccbca69304ca5e8ab1867bf5fc447b2cd8709a
Author: Cool-Coding <ri...@163.com>
AuthorDate: Tue Apr 12 20:12:54 2022 +0800
[Feature] Add JDK ThreadPoolExecutor plugin(#8743) (#146)
---
.github/workflows/plugins-test.3.yaml | 1 +
CHANGES.md | 1 +
.../apm/agent/core/logging/core/WriterFactory.java | 5 +-
.../apm/agent/core/plugin/PluginFinder.java | 9 +
.../agent/core/logging/core/WriterFactoryTest.java | 8 +
.../skywalking/apm/agent/SkyWalkingAgent.java | 2 +
.../jdk-threadpool-plugin/pom.xml | 48 +++
.../plugin/AbstractThreadingPoolInterceptor.java | 62 ++++
.../plugin/ThreadPoolExecuteMethodInterceptor.java | 45 +++
.../plugin/ThreadPoolSubmitMethodInterceptor.java | 47 +++
.../define/ThreadPoolExecutorInstrumentation.java | 96 ++++++
.../apm/plugin/wrapper/SwCallableWrapper.java | 53 +++
.../apm/plugin/wrapper/SwRunnableWrapper.java | 52 +++
.../src/main/resources/skywalking-plugin.def | 17 +
apm-sniffer/bootstrap-plugins/pom.xml | 1 +
.../service-agent/java-agent/Bootstrap-plugins.md | 3 +-
.../setup/service-agent/java-agent/Plugin-list.md | 1 +
.../jdk-threadpool-scenario/bin/startup.sh | 21 ++
.../config/expectedData.yaml | 372 +++++++++++++++++++++
.../jdk-threadpool-scenario/configuration.yml | 22 ++
.../scenarios/jdk-threadpool-scenario/pom.xml | 88 +++++
.../src/main/assembly/assembly.xml | 41 +++
.../apm/testcase/jdk/threading/Application.java | 110 ++++++
.../src/main/resources/application.yaml | 19 ++
.../jdk-threadpool-scenario/support-version.list | 17 +
25 files changed, 1139 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/plugins-test.3.yaml b/.github/workflows/plugins-test.3.yaml
index 1ef64b5da..74419fcc4 100644
--- a/.github/workflows/plugins-test.3.yaml
+++ b/.github/workflows/plugins-test.3.yaml
@@ -98,6 +98,7 @@ jobs:
- tomcat-thread-pool-scenario
- guava-eventbus-scenario
- shenyu-2.4.x-scenario
+ - jdk-threadpool-scenario
steps:
- uses: actions/checkout@v2
with:
diff --git a/CHANGES.md b/CHANGES.md
index 835c8ca2b..9d24594bc 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -36,6 +36,7 @@ Release Notes.
* Make sure the parent endpoint in tracing context from existing first ENTRY span, rather than first span only.
* Fix the bug that maybe causing memory leak and repeated traceId when use gateway-2.1.x-plugin or gateway-3.x-plugin.
* Fix Grpc 1.x plugin could leak context due to gRPC cancelled.
+* Add JDK ThreadPoolExecutor Plugin.
#### Documentation
diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/WriterFactory.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/WriterFactory.java
index e8adf48d2..a835e8cb0 100644
--- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/WriterFactory.java
+++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/WriterFactory.java
@@ -22,6 +22,7 @@ import org.apache.skywalking.apm.agent.core.boot.AgentPackageNotFoundException;
import org.apache.skywalking.apm.agent.core.boot.AgentPackagePath;
import org.apache.skywalking.apm.agent.core.conf.Config;
import org.apache.skywalking.apm.agent.core.conf.SnifferConfigInitializer;
+import org.apache.skywalking.apm.agent.core.plugin.PluginFinder;
import org.apache.skywalking.apm.util.StringUtil;
public class WriterFactory {
@@ -35,7 +36,9 @@ public class WriterFactory {
if (WRITER != null) {
return WRITER;
}
- if (SnifferConfigInitializer.isInitCompleted() && AgentPackagePath.isPathFound()) {
+ if (SnifferConfigInitializer.isInitCompleted()
+ && PluginFinder.isPluginInitCompleted()
+ && AgentPackagePath.isPathFound()) {
if (StringUtil.isEmpty(Config.Logging.DIR)) {
try {
Config.Logging.DIR = AgentPackagePath.getPath() + "/logs";
diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginFinder.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginFinder.java
index 6a2082ae3..4ef608dcc 100644
--- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginFinder.java
+++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/PluginFinder.java
@@ -43,6 +43,7 @@ public class PluginFinder {
private final Map<String, LinkedList<AbstractClassEnhancePluginDefine>> nameMatchDefine = new HashMap<String, LinkedList<AbstractClassEnhancePluginDefine>>();
private final List<AbstractClassEnhancePluginDefine> signatureMatchDefine = new ArrayList<AbstractClassEnhancePluginDefine>();
private final List<AbstractClassEnhancePluginDefine> bootstrapClassMatchDefine = new ArrayList<AbstractClassEnhancePluginDefine>();
+ private static boolean IS_PLUGIN_INIT_COMPLETED = false;
public PluginFinder(List<AbstractClassEnhancePluginDefine> plugins) {
for (AbstractClassEnhancePluginDefine plugin : plugins) {
@@ -107,4 +108,12 @@ public class PluginFinder {
public List<AbstractClassEnhancePluginDefine> getBootstrapClassMatchDefine() {
return bootstrapClassMatchDefine;
}
+
+ public static void pluginInitCompleted() {
+ IS_PLUGIN_INIT_COMPLETED = true;
+ }
+
+ public static boolean isPluginInitCompleted() {
+ return IS_PLUGIN_INIT_COMPLETED;
+ }
}
diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/WriterFactoryTest.java b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/WriterFactoryTest.java
index 0cfd717ad..cc3620a6e 100644
--- a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/WriterFactoryTest.java
+++ b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/WriterFactoryTest.java
@@ -21,6 +21,7 @@ package org.apache.skywalking.apm.agent.core.logging.core;
import org.apache.skywalking.apm.agent.core.boot.AgentPackagePath;
import org.apache.skywalking.apm.agent.core.conf.Config;
import org.apache.skywalking.apm.agent.core.conf.SnifferConfigInitializer;
+import org.apache.skywalking.apm.agent.core.plugin.PluginFinder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.BDDMockito;
@@ -33,6 +34,7 @@ import static org.junit.Assert.assertTrue;
@RunWith(PowerMockRunner.class)
@PrepareForTest(value = {
SnifferConfigInitializer.class,
+ PluginFinder.class,
AgentPackagePath.class
})
public class WriterFactoryTest {
@@ -41,11 +43,14 @@ public class WriterFactoryTest {
public void alwaysReturnSystemLogWriteWithSetLoggingDir() {
Config.Logging.OUTPUT = LogOutput.CONSOLE;
PowerMockito.mockStatic(SnifferConfigInitializer.class);
+ PowerMockito.mockStatic(PluginFinder.class);
PowerMockito.mockStatic(AgentPackagePath.class);
BDDMockito.given(SnifferConfigInitializer.isInitCompleted()).willReturn(true);
+ BDDMockito.given(PluginFinder.isPluginInitCompleted()).willReturn(true);
BDDMockito.given(AgentPackagePath.isPathFound()).willReturn(true);
assertTrue(SnifferConfigInitializer.isInitCompleted());
+ assertTrue(PluginFinder.isPluginInitCompleted());
assertTrue(AgentPackagePath.isPathFound());
IWriter logWriter = WriterFactory.getLogWriter();
@@ -56,11 +61,14 @@ public class WriterFactoryTest {
public void returnFileWriterWriteWithBlankLoggingDir() {
Config.Logging.OUTPUT = LogOutput.FILE;
PowerMockito.mockStatic(SnifferConfigInitializer.class);
+ PowerMockito.mockStatic(PluginFinder.class);
PowerMockito.mockStatic(AgentPackagePath.class);
BDDMockito.given(SnifferConfigInitializer.isInitCompleted()).willReturn(true);
+ BDDMockito.given(PluginFinder.isPluginInitCompleted()).willReturn(true);
BDDMockito.given(AgentPackagePath.isPathFound()).willReturn(true);
assertTrue(SnifferConfigInitializer.isInitCompleted());
+ assertTrue(PluginFinder.isPluginInitCompleted());
assertTrue(AgentPackagePath.isPathFound());
IWriter logWriter = WriterFactory.getLogWriter();
diff --git a/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java b/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java
index 31d586a68..57a11e9f3 100644
--- a/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java
+++ b/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java
@@ -130,6 +130,8 @@ public class SkyWalkingAgent {
.with(new Listener())
.installOn(instrumentation);
+ PluginFinder.pluginInitCompleted();
+
try {
ServiceManager.INSTANCE.boot();
} catch (Exception e) {
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/pom.xml b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/pom.xml
new file mode 100644
index 000000000..d1e0cb3db
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/pom.xml
@@ -0,0 +1,48 @@
+<!--
+ ~ 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.
+ ~
+ -->
+
+<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">
+ <parent>
+ <artifactId>bootstrap-plugins</artifactId>
+ <groupId>org.apache.skywalking</groupId>
+ <version>8.10.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>apm-jdk-threadpool-plugin</artifactId>
+ <packaging>jar</packaging>
+
+ <name>apm-jdk-threadpool-plugin</name>
+ <url>http://maven.apache.org</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-deploy-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/AbstractThreadingPoolInterceptor.java b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/AbstractThreadingPoolInterceptor.java
new file mode 100644
index 000000000..c130c2f5b
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/AbstractThreadingPoolInterceptor.java
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+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.InstanceMethodsAroundInterceptor;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import java.lang.reflect.Method;
+
+public abstract class AbstractThreadingPoolInterceptor implements InstanceMethodsAroundInterceptor {
+ @Override
+ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
+ if (!ContextManager.isActive()) {
+ return;
+ }
+
+ if (allArguments == null || allArguments.length < 1) {
+ return;
+ }
+
+ Object argument = allArguments[0];
+
+ Object wrappedObject = wrap(argument);
+ if (wrappedObject != null) {
+ allArguments[0] = wrappedObject;
+ }
+ }
+
+ /**
+ * wrap the Callable or Runnable object if needed
+ * @param param Callable or Runnable object
+ * @return Wrapped object or null if not needed to wrap
+ */
+ public abstract Object wrap(Object param);
+
+ @Override
+ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {
+ ContextManager.activeSpan().log(t);
+ }
+}
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/ThreadPoolExecuteMethodInterceptor.java b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/ThreadPoolExecuteMethodInterceptor.java
new file mode 100644
index 000000000..077d925bd
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/ThreadPoolExecuteMethodInterceptor.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import org.apache.skywalking.apm.plugin.wrapper.SwRunnableWrapper;
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import java.util.concurrent.RunnableFuture;
+
+public class ThreadPoolExecuteMethodInterceptor extends AbstractThreadingPoolInterceptor {
+
+ @Override
+ public Object wrap(Object param) {
+ if (param instanceof SwRunnableWrapper) {
+ return null;
+ }
+
+ if (param instanceof RunnableFuture) {
+ return null;
+ }
+
+ if (!(param instanceof Runnable)) {
+ return null;
+ }
+
+ Runnable runnable = (Runnable) param;
+ return new SwRunnableWrapper(runnable, ContextManager.capture());
+ }
+
+}
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/ThreadPoolSubmitMethodInterceptor.java b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/ThreadPoolSubmitMethodInterceptor.java
new file mode 100644
index 000000000..bb938dddb
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/ThreadPoolSubmitMethodInterceptor.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.plugin.wrapper.SwCallableWrapper;
+import org.apache.skywalking.apm.plugin.wrapper.SwRunnableWrapper;
+
+import java.util.concurrent.Callable;
+
+public class ThreadPoolSubmitMethodInterceptor extends AbstractThreadingPoolInterceptor {
+
+ @Override
+ public Object wrap(Object param) {
+ if (param instanceof SwRunnableWrapper || param instanceof SwCallableWrapper) {
+ return null;
+ }
+
+ if (param instanceof Callable) {
+ Callable callable = (Callable) param;
+ return new SwCallableWrapper(callable, ContextManager.capture());
+ }
+
+ if (param instanceof Runnable) {
+ Runnable runnable = (Runnable) param;
+ return new SwRunnableWrapper(runnable, ContextManager.capture());
+ }
+
+ return null;
+ }
+}
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/ThreadPoolExecutorInstrumentation.java b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/ThreadPoolExecutorInstrumentation.java
new file mode 100644
index 000000000..d057a8f31
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/define/ThreadPoolExecutorInstrumentation.java
@@ -0,0 +1,96 @@
+/*
+ * 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.define;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import net.bytebuddy.matcher.ElementMatchers;
+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.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
+import org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch;
+import org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch;
+import org.apache.skywalking.apm.agent.core.plugin.match.logical.LogicalMatchOperation;
+
+public class ThreadPoolExecutorInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String ENHANCE_CLASS = "java.util.concurrent.ThreadPoolExecutor";
+
+ private static final String INTERCEPT_EXECUTE_METHOD = "execute";
+
+ private static final String INTERCEPT_SUBMIT_METHOD = "submit";
+
+ private static final String INTERCEPT_EXECUTE_METHOD_HANDLE = "org.apache.skywalking.apm.plugin.ThreadPoolExecuteMethodInterceptor";
+
+ private static final String INTERCEPT_SUBMIT_METHOD_HANDLE = "org.apache.skywalking.apm.plugin.ThreadPoolSubmitMethodInterceptor";
+
+ @Override
+ public boolean isBootstrapInstrumentation() {
+ return true;
+ }
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ return LogicalMatchOperation.or(HierarchyMatch.byHierarchyMatch(ENHANCE_CLASS), MultiClassNameMatch.byMultiClassMatch(ENHANCE_CLASS));
+ }
+
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[0];
+ }
+
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[]{
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher<MethodDescription> getMethodsMatcher() {
+ return ElementMatchers.named(INTERCEPT_EXECUTE_METHOD);
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return INTERCEPT_EXECUTE_METHOD_HANDLE;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return true;
+ }
+ },
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher<MethodDescription> getMethodsMatcher() {
+ return ElementMatchers.named(INTERCEPT_SUBMIT_METHOD);
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return INTERCEPT_SUBMIT_METHOD_HANDLE;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return true;
+ }
+ }
+ };
+ }
+}
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/wrapper/SwCallableWrapper.java b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/wrapper/SwCallableWrapper.java
new file mode 100644
index 000000000..4f42962da
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/wrapper/SwCallableWrapper.java
@@ -0,0 +1,53 @@
+/*
+ * 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.wrapper;
+
+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.network.trace.component.ComponentsDefine;
+import java.util.concurrent.Callable;
+
+public class SwCallableWrapper implements Callable {
+
+ private Callable callable;
+
+ private ContextSnapshot contextSnapshot;
+
+ public SwCallableWrapper(Callable callable, ContextSnapshot contextSnapshot) {
+ this.callable = callable;
+ this.contextSnapshot = contextSnapshot;
+ }
+
+ @Override
+ public Object call() throws Exception {
+ AbstractSpan span = ContextManager.createLocalSpan(getOperationName());
+ span.setComponent(ComponentsDefine.JDK_THREADING);
+ ContextManager.continued(contextSnapshot);
+ try {
+ return callable.call();
+ } finally {
+ ContextManager.stopSpan();
+ }
+ }
+
+ private String getOperationName() {
+ return "SwCallableWrapper/" + Thread.currentThread().getName();
+ }
+}
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/wrapper/SwRunnableWrapper.java b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/wrapper/SwRunnableWrapper.java
new file mode 100644
index 000000000..d3bdb34d9
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/java/org/apache/skywalking/apm/plugin/wrapper/SwRunnableWrapper.java
@@ -0,0 +1,52 @@
+/*
+ * 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.wrapper;
+
+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.network.trace.component.ComponentsDefine;
+
+public class SwRunnableWrapper implements Runnable {
+
+ private Runnable runnable;
+
+ private ContextSnapshot contextSnapshot;
+
+ public SwRunnableWrapper(Runnable runnable, ContextSnapshot contextSnapshot) {
+ this.runnable = runnable;
+ this.contextSnapshot = contextSnapshot;
+ }
+
+ @Override
+ public void run() {
+ AbstractSpan span = ContextManager.createLocalSpan(getOperationName());
+ span.setComponent(ComponentsDefine.JDK_THREADING);
+ ContextManager.continued(contextSnapshot);
+ try {
+ runnable.run();
+ } finally {
+ ContextManager.stopSpan();
+ }
+ }
+
+ private String getOperationName() {
+ return "SwRunnableWrapper/" + Thread.currentThread().getName();
+ }
+}
diff --git a/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/resources/skywalking-plugin.def
new file mode 100644
index 000000000..2162787cd
--- /dev/null
+++ b/apm-sniffer/bootstrap-plugins/jdk-threadpool-plugin/src/main/resources/skywalking-plugin.def
@@ -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.
+
+jdk-threadpool-plugin=org.apache.skywalking.apm.plugin.define.ThreadPoolExecutorInstrumentation
diff --git a/apm-sniffer/bootstrap-plugins/pom.xml b/apm-sniffer/bootstrap-plugins/pom.xml
index 7034b6285..b4f7f0ef8 100644
--- a/apm-sniffer/bootstrap-plugins/pom.xml
+++ b/apm-sniffer/bootstrap-plugins/pom.xml
@@ -44,6 +44,7 @@
<modules>
<module>jdk-http-plugin</module>
<module>jdk-threading-plugin</module>
+ <module>jdk-threadpool-plugin</module>
</modules>
<dependencies>
diff --git a/docs/en/setup/service-agent/java-agent/Bootstrap-plugins.md b/docs/en/setup/service-agent/java-agent/Bootstrap-plugins.md
index 33fbe321f..415ebcdb2 100644
--- a/docs/en/setup/service-agent/java-agent/Bootstrap-plugins.md
+++ b/docs/en/setup/service-agent/java-agent/Bootstrap-plugins.md
@@ -4,4 +4,5 @@ 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.8+
-* Plugin of JDK Callable and Runnable. Agent is compatible with JDK 1.8+
\ No newline at end of file
+* Plugin of JDK Callable and Runnable. Agent is compatible with JDK 1.8+
+* Plugin of JDK ThreadPoolExecutor. Agent is compatible with JDK 1.8+
\ No newline at end of file
diff --git a/docs/en/setup/service-agent/java-agent/Plugin-list.md b/docs/en/setup/service-agent/java-agent/Plugin-list.md
index 213d86b89..442073ee6 100644
--- a/docs/en/setup/service-agent/java-agent/Plugin-list.md
+++ b/docs/en/setup/service-agent/java-agent/Plugin-list.md
@@ -137,3 +137,4 @@
- tomcat-thread-pool
- guava-eventbus
- shenyu-2.4.x
+- jdk-threadpool-plugin
diff --git a/test/plugin/scenarios/jdk-threadpool-scenario/bin/startup.sh b/test/plugin/scenarios/jdk-threadpool-scenario/bin/startup.sh
new file mode 100644
index 000000000..811fbb4ea
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threadpool-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 -jar ${agent_opts} ${home}/../libs/jdk-threadpool-scenario.jar &
\ No newline at end of file
diff --git a/test/plugin/scenarios/jdk-threadpool-scenario/config/expectedData.yaml b/test/plugin/scenarios/jdk-threadpool-scenario/config/expectedData.yaml
new file mode 100644
index 000000000..3a78cdd07
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threadpool-scenario/config/expectedData.yaml
@@ -0,0 +1,372 @@
+# 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.
+segmentItems:
+ - serviceName: jdk-threadpool-scenario
+ segmentSize: ge 14
+ segments:
+ - segmentId: not null
+ spans:
+ - operationName: GET:/threadpool
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 14
+ isError: false
+ spanType: Entry
+ peer: ''
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/threadpool'}
+ - {key: http.method, value: GET}
+ refs:
+ - {parentEndpoint: SwRunnableWrapper/http-get-thread, networkAddress: 'localhost:8080',
+ refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: jdk-threadpool-scenario,
+ traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: /threadpool
+ operationId: 0
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 13
+ isError: false
+ spanType: Exit
+ peer: localhost:8080
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/threadpool'}
+ - {key: http.method, value: GET}
+ - operationName: SwRunnableWrapper/http-get-thread
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Unknown
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 80
+ isError: false
+ spanType: Local
+ peer: ''
+ skipAnalysis: false
+ refs:
+ - {parentEndpoint: 'GET:/greet/{username}', networkAddress: '', refType: CrossThread,
+ parentSpanId: 0, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: jdk-threadpool-scenario,
+ traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: GET:/threadpool
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 14
+ isError: false
+ spanType: Entry
+ peer: ''
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/threadpool'}
+ - {key: http.method, value: GET}
+ refs:
+ - {parentEndpoint: SwCallableWrapper/http-get-thread, networkAddress: 'localhost:8080',
+ refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: jdk-threadpool-scenario,
+ traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: /threadpool
+ operationId: 0
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 13
+ isError: false
+ spanType: Exit
+ peer: localhost:8080
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/threadpool'}
+ - {key: http.method, value: GET}
+ - operationName: SwCallableWrapper/http-get-thread
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Unknown
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 80
+ isError: false
+ spanType: Local
+ peer: ''
+ skipAnalysis: false
+ refs:
+ - {parentEndpoint: 'GET:/greet/{username}', networkAddress: '', refType: CrossThread,
+ parentSpanId: 0, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: jdk-threadpool-scenario,
+ traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: GET:/threadpool
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 14
+ isError: false
+ spanType: Entry
+ peer: ''
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/threadpool'}
+ - {key: http.method, value: GET}
+ refs:
+ - {parentEndpoint: SwRunnableWrapper/http-get-thread, networkAddress: 'localhost:8080',
+ refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: jdk-threadpool-scenario,
+ traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: /threadpool
+ operationId: 0
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 13
+ isError: false
+ spanType: Exit
+ peer: localhost:8080
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/threadpool'}
+ - {key: http.method, value: GET}
+ - operationName: SwRunnableWrapper/http-get-thread
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Unknown
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 80
+ isError: false
+ spanType: Local
+ peer: ''
+ skipAnalysis: false
+ refs:
+ - {parentEndpoint: 'GET:/greet/{username}', networkAddress: '', refType: CrossThread,
+ parentSpanId: 0, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: jdk-threadpool-scenario,
+ traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: /threadpool
+ operationId: 0
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 13
+ isError: false
+ spanType: Exit
+ peer: localhost:8080
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/threadpool'}
+ - {key: http.method, value: GET}
+ - operationName: SwRunnableWrapper/http-get-thread
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Unknown
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 80
+ isError: false
+ spanType: Local
+ peer: ''
+ skipAnalysis: false
+ refs:
+ - {parentEndpoint: 'GET:/greet/{username}', networkAddress: '', refType: CrossThread,
+ parentSpanId: 0, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: jdk-threadpool-scenario,
+ traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: GET:/threadpool
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 14
+ isError: false
+ spanType: Entry
+ peer: ''
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/threadpool'}
+ - {key: http.method, value: GET}
+ refs:
+ - {parentEndpoint: SwCallableWrapper/http-get-thread, networkAddress: 'localhost:8080',
+ refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: jdk-threadpool-scenario,
+ traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: GET:/threadpool
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 14
+ isError: false
+ spanType: Entry
+ peer: ''
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/threadpool'}
+ - {key: http.method, value: GET}
+ refs:
+ - {parentEndpoint: SwRunnableWrapper/http-get-thread, networkAddress: 'localhost:8080',
+ refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: jdk-threadpool-scenario,
+ traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: /threadpool
+ operationId: 0
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 13
+ isError: false
+ spanType: Exit
+ peer: localhost:8080
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/threadpool'}
+ - {key: http.method, value: GET}
+ - operationName: SwCallableWrapper/http-get-thread
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Unknown
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 80
+ isError: false
+ spanType: Local
+ peer: ''
+ skipAnalysis: false
+ refs:
+ - {parentEndpoint: 'GET:/greet/{username}', networkAddress: '', refType: CrossThread,
+ parentSpanId: 0, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: jdk-threadpool-scenario,
+ traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: /threadpool
+ operationId: 0
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 13
+ isError: false
+ spanType: Exit
+ peer: localhost:8080
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/threadpool'}
+ - {key: http.method, value: GET}
+ - operationName: SwRunnableWrapper/http-get-thread
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Unknown
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 80
+ isError: false
+ spanType: Local
+ peer: ''
+ skipAnalysis: false
+ refs:
+ - {parentEndpoint: 'GET:/greet/{username}', networkAddress: '', refType: CrossThread,
+ parentSpanId: 0, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: jdk-threadpool-scenario,
+ traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: GET:/greet/{username}
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 14
+ isError: false
+ spanType: Entry
+ peer: ''
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/greet/skywalking'}
+ - {key: http.method, value: GET}
+ - segmentId: not null
+ spans:
+ - operationName: GET:/threadpool
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 14
+ isError: false
+ spanType: Entry
+ peer: ''
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/threadpool'}
+ - {key: http.method, value: GET}
+ refs:
+ - {parentEndpoint: SwRunnableWrapper/http-get-thread, networkAddress: 'localhost:8080',
+ refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: jdk-threadpool-scenario,
+ traceId: not null}
\ No newline at end of file
diff --git a/test/plugin/scenarios/jdk-threadpool-scenario/configuration.yml b/test/plugin/scenarios/jdk-threadpool-scenario/configuration.yml
new file mode 100644
index 000000000..1b369476a
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threadpool-scenario/configuration.yml
@@ -0,0 +1,22 @@
+# 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-threadpool-plugin-*.jar
+startScript: ./bin/startup.sh
diff --git a/test/plugin/scenarios/jdk-threadpool-scenario/pom.xml b/test/plugin/scenarios/jdk-threadpool-scenario/pom.xml
new file mode 100644
index 000000000..1cb62527a
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threadpool-scenario/pom.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+<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>
+
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>jdk-threadpool-scenario</artifactId>
+ <version>5.0.0</version>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <compiler.version>1.8</compiler.version>
+ <spring.boot.version>2.1.6.RELEASE</spring.boot.version>
+ </properties>
+
+ <name>skywalking-jdk-threadpool-scenario</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ <version>${spring.boot.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <finalName>jdk-threadpool-scenario</finalName>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <version>${spring.boot.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <source>${compiler.version}</source>
+ <target>${compiler.version}</target>
+ <encoding>${project.build.sourceEncoding}</encoding>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/assembly.xml</descriptor>
+ </descriptors>
+ <outputDirectory>./target/</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/test/plugin/scenarios/jdk-threadpool-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/jdk-threadpool-scenario/src/main/assembly/assembly.xml
new file mode 100644
index 000000000..36556b781
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threadpool-scenario/src/main/assembly/assembly.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ ~
+ -->
+<assembly
+ xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <fileSet>
+ <directory>./bin</directory>
+ <fileMode>0775</fileMode>
+ </fileSet>
+ </fileSets>
+
+ <files>
+ <file>
+ <source>./target/jdk-threadpool-scenario.jar</source>
+ <outputDirectory>./libs</outputDirectory>
+ <fileMode>0775</fileMode>
+ </file>
+ </files>
+</assembly>
\ No newline at end of file
diff --git a/test/plugin/scenarios/jdk-threadpool-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/threading/Application.java b/test/plugin/scenarios/jdk-threadpool-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/threading/Application.java
new file mode 100644
index 000000000..094c0aed0
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threadpool-scenario/src/main/java/test/apache/skywalking/apm/testcase/jdk/threading/Application.java
@@ -0,0 +1,110 @@
+/*
+ * 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.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 java.util.concurrent.ThreadFactory;
+
+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;
+
+@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;
+ private final ExecutorService executorService2;
+
+ public TestController(final RestTemplate restTemplate) {
+ this.restTemplate = restTemplate;
+ this.executorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread thread = new Thread(r);
+ thread.setName("http-get-thread");
+ return thread;
+ }
+ });
+ this.executorService2 = Executors.newSingleThreadExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread thread = new Thread(r);
+ thread.setName("http-get-thread");
+ return thread;
+ }
+ });
+ }
+
+ @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("http://localhost:8080/threadpool", String.class);
+ }
+ };
+
+ Callable callable = new Callable<String>() {
+ @Override
+ public String call() {
+ return restTemplate.getForEntity("http://localhost:8080/threadpool", String.class).getBody();
+ }
+ };
+
+ executorService.execute(runnable);
+ executorService.submit(runnable);
+ executorService.submit(callable).get();
+
+ executorService2.execute(runnable);
+ executorService2.submit(runnable);
+ executorService2.submit(callable).get();
+
+ return username;
+ }
+
+ @GetMapping("/threadpool")
+ public String threadpool() {
+ return "threadpool";
+ }
+
+ }
+}
diff --git a/test/plugin/scenarios/jdk-threadpool-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/jdk-threadpool-scenario/src/main/resources/application.yaml
new file mode 100644
index 000000000..6ff39eb8e
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threadpool-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-threadpool-scenario/support-version.list b/test/plugin/scenarios/jdk-threadpool-scenario/support-version.list
new file mode 100644
index 000000000..feef03cde
--- /dev/null
+++ b/test/plugin/scenarios/jdk-threadpool-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