diff --git a/agent-sdk/pom.xml b/agent-sdk/pom.xml
new file mode 100644
index 0000000000000..12cd969354860
--- /dev/null
+++ b/agent-sdk/pom.xml
@@ -0,0 +1,36 @@
+
+
+
+
+ pinpoint
+ com.navercorp.pinpoint
+ 2.3.0-SNAPSHOT
+
+ 4.0.0
+
+ pinpoint-agent-sdk
+ pinpoint-agent-sdk
+
+
+
+
+
+ UTF-8
+
+
+
+
+
+
+
+
+
+
+ maven-deploy-plugin
+ 2.8.2
+
+
+
+
+
diff --git a/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceCallable.java b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceCallable.java
new file mode 100644
index 0000000000000..6092743f1ef0c
--- /dev/null
+++ b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceCallable.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.sdk.v1.concurrent;
+
+import java.util.Objects;
+import java.util.concurrent.Callable;
+
+/**
+ * {@link Callable} for TraceContext propagation
+ * @param return type
+ */
+public class TraceCallable implements Callable {
+
+ public static Callable wrap(Callable delegate) {
+ return new TraceCallable<>(delegate);
+ }
+
+ public static Callable wrapAndExecute(Callable delegate) {
+ return new TraceCallable<>(delegate);
+ }
+
+ protected final Callable delegate;
+
+ public TraceCallable(Callable delegate) {
+ this.delegate = Objects.requireNonNull(delegate, "delegate");
+ }
+
+ @Override
+ public V call() throws Exception {
+ return delegate.call();
+ }
+}
diff --git a/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceExecutor.java b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceExecutor.java
new file mode 100644
index 0000000000000..22f2c155ef93b
--- /dev/null
+++ b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceExecutor.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.sdk.v1.concurrent;
+
+import com.navercorp.pinpoint.sdk.v1.concurrent.wrapper.CommandWrapper;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Executor for TraceContext propagation.
+ */
+public class TraceExecutor implements Executor {
+
+ protected final Executor delegate;
+ protected final CommandWrapper wrapper;
+
+ public TraceExecutor(Executor delegate, CommandWrapper wrapper) {
+ this.delegate = Objects.requireNonNull(delegate, "delegate");
+ this.wrapper = Objects.requireNonNull(wrapper, "wrapper");
+ }
+
+
+ @Override
+ public void execute(Runnable command) {
+ command = wrapper.wrap(command);
+ delegate.execute(command);
+ }
+
+}
diff --git a/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceExecutorService.java b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceExecutorService.java
new file mode 100644
index 0000000000000..d7b1b3698730e
--- /dev/null
+++ b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceExecutorService.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2021 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.sdk.v1.concurrent;
+
+import com.navercorp.pinpoint.sdk.v1.concurrent.wrapper.CommandWrapper;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * {@link ExecutorService} for TraceContext propagation.
+ */
+public class TraceExecutorService implements ExecutorService {
+ protected final ExecutorService delegate;
+ protected final CommandWrapper wrapper;
+
+ public TraceExecutorService(ExecutorService delegate, CommandWrapper wrapper) {
+ this.delegate = Objects.requireNonNull(delegate, "delegate");
+ this.wrapper = Objects.requireNonNull(wrapper, "wrapper");
+ }
+
+ @Override
+ public void shutdown() {
+ delegate.shutdown();
+ }
+
+ @Override
+ public List shutdownNow() {
+ return delegate.shutdownNow();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return delegate.isShutdown();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return delegate.isTerminated();
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ return delegate.awaitTermination(timeout, unit);
+ }
+
+ @Override
+ public Future submit(Callable task) {
+ task = wrapper.wrap(task);
+ return delegate.submit(task);
+ }
+
+ @Override
+ public Future submit(Runnable task, T result) {
+ task = wrapper.wrap(task);
+ return delegate.submit(task, result);
+ }
+
+ @Override
+ public Future> submit(Runnable task) {
+ task = wrapper.wrap(task);
+ return delegate.submit(task);
+ }
+
+ @Override
+ public List> invokeAll(Collection extends Callable> tasks) throws InterruptedException {
+ tasks = wrapper.wrap(tasks);
+ return delegate.invokeAll(tasks);
+ }
+
+ @Override
+ public List> invokeAll(Collection extends Callable> tasks, long timeout, TimeUnit unit) throws InterruptedException {
+ tasks = wrapper.wrap(tasks);
+ return delegate.invokeAll(tasks, timeout, unit);
+ }
+
+ @Override
+ public T invokeAny(Collection extends Callable> tasks) throws InterruptedException, ExecutionException {
+ tasks = wrapper.wrap(tasks);
+ return delegate.invokeAny(tasks);
+ }
+
+ @Override
+ public T invokeAny(Collection extends Callable> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ tasks = wrapper.wrap(tasks);
+ return delegate.invokeAny(tasks, timeout, unit);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ command = wrapper.wrap(command);
+ delegate.execute(command);
+ }
+
+}
diff --git a/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceExecutors.java b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceExecutors.java
new file mode 100644
index 0000000000000..38ca44effd4a7
--- /dev/null
+++ b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceExecutors.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2021 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.sdk.v1.concurrent;
+
+import com.navercorp.pinpoint.sdk.v1.concurrent.wrapper.DefaultCommandWrapper;
+import com.navercorp.pinpoint.sdk.v1.concurrent.wrapper.DisableCommandWrapper;
+import com.navercorp.pinpoint.sdk.v1.concurrent.wrapper.CommandWrapper;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledExecutorService;
+
+/**
+ * Utility class for Executor
+ */
+public class TraceExecutors {
+
+ public static Executor wrapExecutor(Executor executor) {
+ return wrapExecutor(executor, false);
+ }
+
+ public static Executor wrapExecutor(Executor executor, boolean threadContextPropagation) {
+ Objects.requireNonNull(executor, "executor");
+
+ CommandWrapper wrapper = newCommandWrapper(threadContextPropagation);
+ return new TraceExecutor(executor, wrapper);
+ }
+
+ public static ExecutorService wrapExecutorService(ExecutorService executorService) {
+ return wrapExecutorService(executorService, false);
+ }
+
+ public static ExecutorService wrapExecutorService(ExecutorService executorService, boolean threadContextPropagation) {
+ Objects.requireNonNull(executorService, "executorService");
+
+ CommandWrapper wrapper = newCommandWrapper(threadContextPropagation);
+ return new TraceExecutorService(executorService, wrapper);
+ }
+
+ public static ScheduledExecutorService wrapScheduledExecutorService(ScheduledExecutorService executorService) {
+ return wrapScheduledExecutorService(executorService, false);
+ }
+
+ public static ScheduledExecutorService wrapScheduledExecutorService(ScheduledExecutorService executorService, boolean threadContextPropagation) {
+ Objects.requireNonNull(executorService, "executorService");
+
+ CommandWrapper wrapper = newCommandWrapper(threadContextPropagation);
+ return new TraceScheduledExecutorService(executorService, wrapper);
+ }
+
+ private static CommandWrapper newCommandWrapper(boolean threadContextPropagation) {
+ if (threadContextPropagation) {
+ return new DefaultCommandWrapper();
+ }
+ return new DisableCommandWrapper();
+ }
+
+}
diff --git a/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceForkJoinPool.java b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceForkJoinPool.java
new file mode 100644
index 0000000000000..fc98b6e06401b
--- /dev/null
+++ b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceForkJoinPool.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2021 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.sdk.v1.concurrent;
+
+import com.navercorp.pinpoint.sdk.v1.concurrent.wrapper.DefaultCommandWrapper;
+import com.navercorp.pinpoint.sdk.v1.concurrent.wrapper.CommandWrapper;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Not open yet.
+ */
+class TraceForkJoinPool extends ForkJoinPool {
+
+ protected CommandWrapper commandWrapper = new DefaultCommandWrapper();
+
+ private TraceForkJoinPool() {
+ }
+
+ private TraceForkJoinPool(int parallelism) {
+ super(parallelism);
+ }
+
+
+ private TraceForkJoinPool(int parallelism,
+ ForkJoinWorkerThreadFactory factory,
+ Thread.UncaughtExceptionHandler handler,
+ boolean asyncMode) {
+ super(parallelism, factory, handler, asyncMode);
+ }
+
+
+
+ protected ForkJoinTask wrap(ForkJoinTask task) {
+ // TODO How to delegate ForkJoinTask?
+ return task;
+ }
+
+
+
+ public ForkJoinTask submit(ForkJoinTask task) {
+ task = wrap(task);
+ return super.submit(task);
+ }
+
+
+ public ForkJoinTask submit(Callable task) {
+ task = commandWrapper.wrap(task);
+ return super.submit(task);
+ }
+
+
+ public ForkJoinTask submit(Runnable task, T result) {
+ task = commandWrapper.wrap(task);
+ return super.submit(task, result);
+ }
+
+
+ public ForkJoinTask> submit(Runnable task) {
+ task = commandWrapper.wrap(task);
+ return super.submit(task);
+ }
+
+ @Override
+ public T invokeAny(Collection extends Callable> tasks) throws InterruptedException, ExecutionException {
+ tasks = commandWrapper.wrap(tasks);
+ return super.invokeAny(tasks);
+ }
+
+ @Override
+ public T invokeAny(Collection extends Callable> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ tasks = commandWrapper.wrap(tasks);
+ return super.invokeAny(tasks, timeout, unit);
+ }
+
+ @Override
+ public List> invokeAll(Collection extends Callable> tasks, long timeout, TimeUnit unit) throws InterruptedException {
+ tasks = commandWrapper.wrap(tasks);
+ return super.invokeAll(tasks, timeout, unit);
+ }
+
+ public void execute(ForkJoinTask> task) {
+ task = wrap(task);
+ super.execute(task);
+ }
+
+
+ public void execute(Runnable task) {
+ task = commandWrapper.wrap(task);
+ super.execute(task);
+ }
+
+}
diff --git a/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceRunnable.java b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceRunnable.java
new file mode 100644
index 0000000000000..814421110e459
--- /dev/null
+++ b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceRunnable.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.sdk.v1.concurrent;
+
+import java.util.Objects;
+
+/**
+ * {@link Runnable} for TraceContext propagation
+ */
+public class TraceRunnable implements Runnable {
+
+ public static Runnable wrap(Runnable delegate) {
+ return new TraceRunnable(delegate);
+ }
+
+ public static Runnable wrapAndExecute(Runnable delegate) {
+ return new TraceRunnable(delegate);
+ }
+
+ protected final Runnable delegate;
+
+ public TraceRunnable(Runnable runnable) {
+ this.delegate = Objects.requireNonNull(runnable, "delegate");
+ }
+
+
+
+ @Override
+ public void run() {
+ this.delegate.run();
+ }
+}
diff --git a/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceScheduledExecutorService.java b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceScheduledExecutorService.java
new file mode 100644
index 0000000000000..9d8fdd97c7433
--- /dev/null
+++ b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/TraceScheduledExecutorService.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2021 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.sdk.v1.concurrent;
+
+import com.navercorp.pinpoint.sdk.v1.concurrent.wrapper.CommandWrapper;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * {@link ScheduledExecutorService} for TraceContext propagation.
+ */
+public class TraceScheduledExecutorService implements ScheduledExecutorService {
+
+ protected final ScheduledExecutorService delegate;
+ protected final CommandWrapper wrapper;
+
+ public TraceScheduledExecutorService(ScheduledExecutorService delegate, CommandWrapper wrapper) {
+ this.delegate = Objects.requireNonNull(delegate, "delegate");
+ this.wrapper = Objects.requireNonNull(wrapper, "wrapper");
+ }
+
+ @Override
+ public ScheduledFuture> schedule(Runnable command, long delay, TimeUnit unit) {
+ command = wrapper.wrap(command);
+ return delegate.schedule(command, delay, unit);
+ }
+
+
+ @Override
+ public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) {
+ callable = wrapper.wrap(callable);
+ return delegate.schedule(callable, delay, unit);
+ }
+
+ @Override
+ public ScheduledFuture> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
+ command = wrapper.wrap(command);
+ return delegate.scheduleAtFixedRate(command, initialDelay, period, unit);
+ }
+
+ @Override
+ public ScheduledFuture> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
+ command = wrapper.wrap(command);
+ return delegate.scheduleWithFixedDelay(command, initialDelay, delay, unit);
+ }
+
+ @Override
+ public void shutdown() {
+ delegate.shutdown();
+ }
+
+ @Override
+ public List shutdownNow() {
+ return delegate.shutdownNow();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return delegate.isShutdown();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return delegate.isTerminated();
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ return delegate.awaitTermination(timeout, unit);
+ }
+
+ @Override
+ public Future submit(Callable task) {
+ task = wrapper.wrap(task);
+ return delegate.submit(task);
+ }
+
+ @Override
+ public Future submit(Runnable task, T result) {
+ task = wrapper.wrap(task);
+ return delegate.submit(task, result);
+ }
+
+ @Override
+ public Future> submit(Runnable task) {
+ task = wrapper.wrap(task);
+ return delegate.submit(task);
+ }
+
+ @Override
+ public List> invokeAll(Collection extends Callable> tasks) throws InterruptedException {
+// tasks = wrapper.wrap(tasks);
+// return delegate.invokeAll(wrapTasks);
+
+ return delegate.invokeAll(tasks);
+ }
+
+
+
+ @Override
+ public List> invokeAll(Collection extends Callable> tasks, long timeout, TimeUnit unit) throws InterruptedException {
+// tasks = wrapper.wrap(tasks);
+// return delegate.invokeAll(tasks, timeout, unit);
+
+ return delegate.invokeAll(tasks, timeout, unit);
+ }
+
+ @Override
+ public T invokeAny(Collection extends Callable> tasks) throws InterruptedException, ExecutionException {
+// tasks = wrapper.wrap(tasks);
+// return delegate.invokeAny(wrapTasks);
+
+ return delegate.invokeAny(tasks);
+ }
+
+ @Override
+ public T invokeAny(Collection extends Callable> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+// tasks = wrapper.wrap(tasks);
+// return delegate.invokeAny(wrapTasks, timeout, unit);
+
+ return delegate.invokeAny(tasks, timeout, unit);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ command = wrapper.wrap(command);
+ delegate.execute(command);
+ }
+}
diff --git a/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/wrapper/CommandWrapper.java b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/wrapper/CommandWrapper.java
new file mode 100644
index 0000000000000..29616190aeb4b
--- /dev/null
+++ b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/wrapper/CommandWrapper.java
@@ -0,0 +1,12 @@
+package com.navercorp.pinpoint.sdk.v1.concurrent.wrapper;
+
+import java.util.Collection;
+import java.util.concurrent.Callable;
+
+public interface CommandWrapper {
+ Runnable wrap(Runnable command);
+
+ Callable wrap(Callable callable);
+
+ Collection extends Callable> wrap(Collection extends Callable> tasks);
+}
diff --git a/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/wrapper/DefaultCommandWrapper.java b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/wrapper/DefaultCommandWrapper.java
new file mode 100644
index 0000000000000..c25843d0f1f52
--- /dev/null
+++ b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/wrapper/DefaultCommandWrapper.java
@@ -0,0 +1,35 @@
+package com.navercorp.pinpoint.sdk.v1.concurrent.wrapper;
+
+import com.navercorp.pinpoint.sdk.v1.concurrent.TraceCallable;
+import com.navercorp.pinpoint.sdk.v1.concurrent.TraceRunnable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+public class DefaultCommandWrapper implements CommandWrapper {
+
+ public Runnable wrap(Runnable command) {
+ if (command instanceof TraceRunnable) {
+ return command;
+ }
+ return new TraceRunnable(command);
+ }
+
+ public Callable wrap(Callable callable) {
+ if (callable instanceof TraceCallable) {
+ return callable;
+ }
+ return new TraceCallable<>(callable);
+ }
+
+ public Collection extends Callable> wrap(Collection extends Callable> tasks) {
+ final List> wrapList = new ArrayList<>(tasks.size());
+ for (Callable task : tasks) {
+ Callable wrapTask = wrap(task);
+ wrapList.add(wrapTask);
+ }
+ return wrapList;
+ }
+}
diff --git a/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/wrapper/DisableCommandWrapper.java b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/wrapper/DisableCommandWrapper.java
new file mode 100644
index 0000000000000..b2719d236d187
--- /dev/null
+++ b/agent-sdk/src/main/java/com/navercorp/pinpoint/sdk/v1/concurrent/wrapper/DisableCommandWrapper.java
@@ -0,0 +1,21 @@
+package com.navercorp.pinpoint.sdk.v1.concurrent.wrapper;
+
+import java.util.Collection;
+import java.util.concurrent.Callable;
+
+public class DisableCommandWrapper implements CommandWrapper {
+ @Override
+ public Runnable wrap(Runnable command) {
+ return command;
+ }
+
+ @Override
+ public Callable wrap(Callable callable) {
+ return callable;
+ }
+
+ @Override
+ public Collection extends Callable> wrap(Collection extends Callable> tasks) {
+ return tasks;
+ }
+}
diff --git a/agent-testweb/agentsdk-asnc-testweb/README.md b/agent-testweb/agentsdk-asnc-testweb/README.md
new file mode 100644
index 0000000000000..cb7393c1fbdab
--- /dev/null
+++ b/agent-testweb/agentsdk-asnc-testweb/README.md
@@ -0,0 +1,16 @@
+
+## Install
+```
+$ mvnw -P pinpoint-agentsdk-async-testweb install -Dmaven.test.skip=true
+```
+
+## Run
+```
+$ mvnw -P pinpoint-agentsdk-async-testweb spring-boot:start
+```
+You can then access here: http://localhost:18080/
+
+## Stop
+```
+$ mvnw -P pinpoint-agentsdk-async-testweb spring-boot:stop
+```
diff --git a/agent-testweb/agentsdk-asnc-testweb/pom.xml b/agent-testweb/agentsdk-asnc-testweb/pom.xml
new file mode 100644
index 0000000000000..c5275c3ac5b1f
--- /dev/null
+++ b/agent-testweb/agentsdk-asnc-testweb/pom.xml
@@ -0,0 +1,29 @@
+
+
+ 4.0.0
+
+ com.navercorp.pinpoint
+ pinpoint-agent-testweb
+ 2.3.0-SNAPSHOT
+
+
+ pinpoint-agentsdk-async-testweb
+
+ jar
+
+
+
+ ${pinpoint.agent.default.jvmargument}
+
+
+
+
+
+ com.navercorp.pinpoint
+ pinpoint-agent-sdk
+ ${project.version}
+
+
+
+
\ No newline at end of file
diff --git a/agent-testweb/agentsdk-asnc-testweb/src/main/java/com/pinpointest/plugin/SdkAsyncPluginTestStarter.java b/agent-testweb/agentsdk-asnc-testweb/src/main/java/com/pinpointest/plugin/SdkAsyncPluginTestStarter.java
new file mode 100644
index 0000000000000..747a726aa501c
--- /dev/null
+++ b/agent-testweb/agentsdk-asnc-testweb/src/main/java/com/pinpointest/plugin/SdkAsyncPluginTestStarter.java
@@ -0,0 +1,12 @@
+package com.pinpointest.plugin;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SdkAsyncPluginTestStarter {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SdkAsyncPluginTestStarter.class, args);
+ }
+}
diff --git a/agent-testweb/agentsdk-asnc-testweb/src/main/java/com/pinpointest/plugin/controller/ThreadContextPropagationController.java b/agent-testweb/agentsdk-asnc-testweb/src/main/java/com/pinpointest/plugin/controller/ThreadContextPropagationController.java
new file mode 100644
index 0000000000000..add7a3a138570
--- /dev/null
+++ b/agent-testweb/agentsdk-asnc-testweb/src/main/java/com/pinpointest/plugin/controller/ThreadContextPropagationController.java
@@ -0,0 +1,72 @@
+package com.pinpointest.plugin.controller;
+
+import com.navercorp.pinpoint.sdk.v1.concurrent.TraceExecutors;
+import com.navercorp.pinpoint.sdk.v1.concurrent.TraceRunnable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.PreDestroy;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+@RestController
+public class ThreadContextPropagationController {
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final ExecutorService traceExecutor = TraceExecutors.wrapExecutorService(Executors.newSingleThreadExecutor());
+
+ @GetMapping(value = "/sdk-async-plugin/manual-context-propagation")
+ public String manualWrapExecute() throws InterruptedException, ExecutionException {
+ CompletableFuture future = new CompletableFuture<>();
+
+ traceExecutor.execute(TraceRunnable.wrap(() -> future.complete("manual-execute")));
+
+ sleep(1000);
+
+ return future.get();
+ }
+
+ private final ExecutorService contextPropagationExecutor = TraceExecutors.wrapExecutorService(Executors.newSingleThreadExecutor(), true);
+
+ @GetMapping(value = "/sdk-async-plugin/auto-context-propagation")
+ public String autoWrapExecute() throws InterruptedException, ExecutionException {
+ CompletableFuture future = new CompletableFuture<>();
+
+ contextPropagationExecutor.execute(() -> future.complete("auto-execute"));
+
+ sleep(1000);
+
+ return future.get();
+ }
+
+ private final ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ @GetMapping(value = "/sdk-async-plugin/traceRunnable-propagation")
+ public String traceRunnableExecute() throws InterruptedException, ExecutionException {
+ CompletableFuture future = new CompletableFuture<>();
+
+ executor.execute(TraceRunnable.wrapAndExecute(() -> future.complete("manual-execute")));
+
+ sleep(1000);
+
+ return future.get();
+ }
+
+ private void sleep(long millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @PreDestroy
+ private void shutdown() {
+ this.traceExecutor.shutdown();
+ this.contextPropagationExecutor.shutdown();
+ }
+}
diff --git a/agent-testweb/agentsdk-asnc-testweb/src/main/resources/application.yml b/agent-testweb/agentsdk-asnc-testweb/src/main/resources/application.yml
new file mode 100644
index 0000000000000..2416cb576dc94
--- /dev/null
+++ b/agent-testweb/agentsdk-asnc-testweb/src/main/resources/application.yml
@@ -0,0 +1,12 @@
+# Defined in commandlineArgument of agent-test pom.xml
+
+#server:
+# port: 18080
+
+#logging:
+# level:
+# root: info
+
+#springdoc:
+# swagger-ui:
+# path: /
\ No newline at end of file
diff --git a/agent-testweb/pom.xml b/agent-testweb/pom.xml
index 2200186e5e0eb..4151bb5728d3e 100644
--- a/agent-testweb/pom.xml
+++ b/agent-testweb/pom.xml
@@ -51,6 +51,7 @@
+ agentsdk-asnc-testweb
thread-plugin-testweb
paho-mqtt-plugin-testweb
reactor-netty-plugin-testweb
diff --git a/bootstraps/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/instrument/transformer/TransformTemplate.java b/bootstraps/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/instrument/transformer/TransformTemplate.java
index fa9b9221dca2f..6194cb7f05626 100644
--- a/bootstraps/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/instrument/transformer/TransformTemplate.java
+++ b/bootstraps/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/instrument/transformer/TransformTemplate.java
@@ -85,8 +85,6 @@ public void transform(String className, Class extends TransformCallback> trans
Objects.requireNonNull(className, "className");
Objects.requireNonNull(transformCallbackClass, "transformCallbackClass");
-
-
TransformCallbackChecker.validate(transformCallbackClass, parameterTypes);
if (ParameterUtils.hasNull(parameterTypes)) {
throw new IllegalArgumentException("null parameterType not supported");
diff --git a/plugins/agentsdk-async/pom.xml b/plugins/agentsdk-async/pom.xml
new file mode 100644
index 0000000000000..e893b2468efb2
--- /dev/null
+++ b/plugins/agentsdk-async/pom.xml
@@ -0,0 +1,24 @@
+
+
+
+
+ com.navercorp.pinpoint
+ pinpoint-plugins
+ 2.3.0-SNAPSHOT
+
+
+ 4.0.0
+ pinpoint-agentsdk-async-plugin
+ jar
+
+
+
+ com.navercorp.pinpoint
+ pinpoint-bootstrap-core
+ provided
+
+
+
+
+
diff --git a/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/AgentSdkAsyncConfig.java b/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/AgentSdkAsyncConfig.java
new file mode 100644
index 0000000000000..9b6844017ceb6
--- /dev/null
+++ b/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/AgentSdkAsyncConfig.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2021 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.plugin.sdk;
+
+import com.navercorp.pinpoint.bootstrap.config.ProfilerConfig;
+
+public class AgentSdkAsyncConfig {
+
+ private final boolean enable;
+
+
+ public AgentSdkAsyncConfig(ProfilerConfig config) {
+ this.enable = config.readBoolean("profiler.agentsdk.enable", true);
+ }
+
+ public boolean isEnable() {
+ return enable;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("AgentSdkConfig{");
+ sb.append("enable=").append(enable);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/AgentSdkAsyncConstants.java b/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/AgentSdkAsyncConstants.java
new file mode 100644
index 0000000000000..86051f32f052e
--- /dev/null
+++ b/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/AgentSdkAsyncConstants.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2021 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.plugin.sdk;
+
+import com.navercorp.pinpoint.common.trace.ServiceType;
+import com.navercorp.pinpoint.common.trace.ServiceTypeFactory;
+
+public class AgentSdkAsyncConstants {
+ public static final ServiceType THREAD_POOL_EXECUTOR = ServiceTypeFactory.of(5081, "THREAD_POOL_EXECUTOR", "THREAD_POOL_EXECUTOR");
+ public static final String THREAD_POOL_EXECUTOR_SCOPE = "ThreadPoolExecutorScope";
+}
diff --git a/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/AgentSdkAsyncPlugin.java b/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/AgentSdkAsyncPlugin.java
new file mode 100644
index 0000000000000..2a4fe4ac90c81
--- /dev/null
+++ b/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/AgentSdkAsyncPlugin.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2021 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.plugin.sdk;
+
+import com.navercorp.pinpoint.bootstrap.async.AsyncContextAccessor;
+import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass;
+import com.navercorp.pinpoint.bootstrap.instrument.InstrumentException;
+import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod;
+import com.navercorp.pinpoint.bootstrap.instrument.Instrumentor;
+import com.navercorp.pinpoint.bootstrap.instrument.MethodFilters;
+import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformCallback;
+import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplate;
+import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplateAware;
+import com.navercorp.pinpoint.bootstrap.logging.PLogger;
+import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
+import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin;
+import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPluginSetupContext;
+import com.navercorp.pinpoint.plugin.sdk.interceptor.TaskCallInterceptor;
+import com.navercorp.pinpoint.plugin.sdk.interceptor.ThreadPoolExecutorSubmitInterceptor;
+
+
+import java.security.ProtectionDomain;
+import java.util.Objects;
+
+public class AgentSdkAsyncPlugin implements ProfilerPlugin, TransformTemplateAware {
+ private final PLogger logger = PLoggerFactory.getLogger(getClass());
+
+ private TransformTemplate transformTemplate;
+
+
+ @Override
+ public void setup(ProfilerPluginSetupContext context) {
+ AgentSdkAsyncConfig agentSdkAsyncConfig = new AgentSdkAsyncConfig(context.getConfig());
+ if (!agentSdkAsyncConfig.isEnable()) {
+ logger.debug("AgentSdkAsyncConfig is disable");
+ return;
+ }
+
+ logger.debug("AgentSdkAsyncConfig config={}", agentSdkAsyncConfig);
+
+ String sdkPackage = "com.navercorp.pinpoint.sdk.v1.concurrent";
+
+ addExecutionInterceptorTask(sdkPackage + ".TraceCallable", "call");
+ addExecutionInterceptorTask(sdkPackage + ".TraceRunnable", "run");
+
+
+ addTraceExecutor(sdkPackage + ".TraceExecutorService");
+ addTraceExecutor(sdkPackage + ".TraceScheduledExecutorService");
+ addTraceExecutor(sdkPackage + ".TraceExecutor");
+
+// addAsyncTaskExecutor(sdkPackage + ".TraceForkJoinPool");
+
+ }
+
+
+ private void addExecutionInterceptorTask(final String className, final String methodName) {
+ Object[] parameter = {methodName};
+ Class>[] parameterType = {String.class};
+ transformTemplate.transform(className, TraceCommandCallback.class, parameter, parameterType);
+ }
+
+ public static class TraceCommandCallback implements TransformCallback {
+ private final String methodName;
+
+ public TraceCommandCallback(String methodName) {
+ this.methodName = Objects.requireNonNull(methodName, "methodName");
+ }
+
+ @Override
+ public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
+ final InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer);
+ target.addField(AsyncContextAccessor.class);
+ final InstrumentMethod callMethod = target.getDeclaredMethod(methodName);
+ if (callMethod != null) {
+ callMethod.addInterceptor(TaskCallInterceptor.class);
+ }
+ return target.toBytecode();
+ }
+ }
+
+
+ private void addTraceExecutor(final String className) {
+ transformTemplate.transform(className, AsyncRunTransformCallback.class);
+ }
+
+ public static class AsyncRunTransformCallback implements TransformCallback {
+ private final PLogger logger = PLoggerFactory.getLogger(getClass());
+
+ @Override
+ public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
+ final InstrumentClass target = instrumentor.getInstrumentClass(classLoader, className, classfileBuffer);
+ for (InstrumentMethod m : target.getDeclaredMethods(MethodFilters.name("execute"))) {
+ m.addScopedInterceptor(ThreadPoolExecutorSubmitInterceptor.class, AgentSdkAsyncConstants.THREAD_POOL_EXECUTOR_SCOPE);
+ }
+ for (InstrumentMethod m : target.getDeclaredMethods(MethodFilters.name("submit"))) {
+ m.addScopedInterceptor(ThreadPoolExecutorSubmitInterceptor.class, AgentSdkAsyncConstants.THREAD_POOL_EXECUTOR_SCOPE);
+ }
+ for (InstrumentMethod m : target.getDeclaredMethods(MethodFilters.name("schedule"))) {
+ m.addScopedInterceptor(ThreadPoolExecutorSubmitInterceptor.class, AgentSdkAsyncConstants.THREAD_POOL_EXECUTOR_SCOPE);
+ }
+ for (InstrumentMethod m : target.getDeclaredMethods(MethodFilters.name("scheduleAtFixedRate"))) {
+ m.addScopedInterceptor(ThreadPoolExecutorSubmitInterceptor.class, AgentSdkAsyncConstants.THREAD_POOL_EXECUTOR_SCOPE);
+ }
+ for (InstrumentMethod m : target.getDeclaredMethods(MethodFilters.name("scheduleWithFixedDelay"))) {
+ m.addScopedInterceptor(ThreadPoolExecutorSubmitInterceptor.class, AgentSdkAsyncConstants.THREAD_POOL_EXECUTOR_SCOPE);
+ }
+// for (InstrumentMethod m : target.getDeclaredMethods(MethodFilters.name("invokeAll"))) {
+// m.addScopedInterceptor(ThreadPoolExecutorSubmitInterceptor.class, AgentSdkAsyncConstants.THREAD_POOL_EXECUTOR_SCOPE);
+// }
+// for (InstrumentMethod m : target.getDeclaredMethods(MethodFilters.name("invokeAny"))) {
+// m.addScopedInterceptor(ThreadPoolExecutorSubmitInterceptor.class, AgentSdkAsyncConstants.THREAD_POOL_EXECUTOR_SCOPE);
+// }
+ return target.toBytecode();
+ }
+ }
+
+
+ @Override
+ public void setTransformTemplate(TransformTemplate transformTemplate) {
+ this.transformTemplate = transformTemplate;
+ }
+}
diff --git a/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/AgentSdkAsyncTraceMetadataProvider.java b/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/AgentSdkAsyncTraceMetadataProvider.java
new file mode 100644
index 0000000000000..9ed121dbe35c8
--- /dev/null
+++ b/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/AgentSdkAsyncTraceMetadataProvider.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2021 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.plugin.sdk;
+
+import com.navercorp.pinpoint.common.trace.TraceMetadataProvider;
+import com.navercorp.pinpoint.common.trace.TraceMetadataSetupContext;
+
+public class AgentSdkAsyncTraceMetadataProvider implements TraceMetadataProvider {
+ @Override
+ public void setup(TraceMetadataSetupContext context) {
+ context.addServiceType(AgentSdkAsyncConstants.THREAD_POOL_EXECUTOR);
+ }
+}
diff --git a/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/interceptor/TaskCallInterceptor.java b/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/interceptor/TaskCallInterceptor.java
new file mode 100644
index 0000000000000..94d477e7a5362
--- /dev/null
+++ b/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/interceptor/TaskCallInterceptor.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.plugin.sdk.interceptor;
+
+import com.navercorp.pinpoint.bootstrap.context.AsyncContext;
+import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor;
+import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder;
+import com.navercorp.pinpoint.bootstrap.context.TraceContext;
+import com.navercorp.pinpoint.bootstrap.interceptor.AsyncContextSpanEventSimpleAroundInterceptor;
+import com.navercorp.pinpoint.bootstrap.logging.PLogger;
+import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
+import com.navercorp.pinpoint.plugin.sdk.AgentSdkAsyncConstants;
+
+
+public class TaskCallInterceptor extends AsyncContextSpanEventSimpleAroundInterceptor {
+ private final PLogger logger = PLoggerFactory.getLogger(this.getClass());
+ public TaskCallInterceptor(TraceContext traceContext, MethodDescriptor methodDescriptor) {
+ super(traceContext, methodDescriptor);
+ }
+
+ @Override
+ protected void doInBeforeTrace(SpanEventRecorder recorder, AsyncContext asyncContext, Object target, Object[] args) {
+ }
+
+ @Override
+ protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) {
+ recorder.recordApi(methodDescriptor);
+ recorder.recordServiceType(AgentSdkAsyncConstants.THREAD_POOL_EXECUTOR);
+ recorder.recordException(throwable);
+ }
+}
diff --git a/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/interceptor/ThreadPoolExecutorSubmitInterceptor.java b/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/interceptor/ThreadPoolExecutorSubmitInterceptor.java
new file mode 100644
index 0000000000000..ecb733409fb77
--- /dev/null
+++ b/plugins/agentsdk-async/src/main/java/com/navercorp/pinpoint/plugin/sdk/interceptor/ThreadPoolExecutorSubmitInterceptor.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2021 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.plugin.sdk.interceptor;
+
+import com.navercorp.pinpoint.bootstrap.async.AsyncContextAccessor;
+import com.navercorp.pinpoint.bootstrap.context.*;
+import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor;
+import com.navercorp.pinpoint.bootstrap.logging.PLogger;
+import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
+import com.navercorp.pinpoint.plugin.sdk.AgentSdkAsyncConstants;
+
+public class ThreadPoolExecutorSubmitInterceptor implements AroundInterceptor {
+
+ private final PLogger logger = PLoggerFactory.getLogger(this.getClass());
+ private final boolean isDebug = logger.isDebugEnabled();
+
+ private TraceContext traceContext;
+ private MethodDescriptor descriptor;
+
+ public ThreadPoolExecutorSubmitInterceptor(TraceContext traceContext, MethodDescriptor descriptor) {
+ this.traceContext = traceContext;
+ this.descriptor = descriptor;
+ }
+
+ @Override
+ public void before(Object target, Object[] args) {
+ if (isDebug) {
+ logger.beforeInterceptor(target, args);
+ }
+
+ final Trace trace = traceContext.currentTraceObject();
+ if (trace == null) {
+ return;
+ }
+
+ final SpanEventRecorder recorder = trace.traceBlockBegin();
+
+
+ boolean r = validate(args);
+ if (r) {
+ // make asynchronous trace-id
+ final AsyncContext asyncContext = recorder.recordNextAsyncContext();
+ ((AsyncContextAccessor) args[0])._$PINPOINT$_setAsyncContext(asyncContext);
+ if (isDebug) {
+ logger.debug("Set asyncContext {}", asyncContext);
+ }
+ }
+ }
+
+ private boolean validate(final Object[] args) {
+ if (args == null || args.length < 1) {
+ if (isDebug) {
+ logger.debug("Invalid args object. args={}.", args);
+ }
+ return false;
+ }
+
+ if (!(args[0] instanceof AsyncContextAccessor)) {
+ if (isDebug) {
+ logger.debug("Invalid args[0] object. Need metadata accessor({}).", AsyncContextAccessor.class.getName());
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void after(Object target, Object[] args, Object result, Throwable throwable) {
+ if (isDebug) {
+ logger.afterInterceptor(target, args, result, throwable);
+ }
+
+ final Trace trace = traceContext.currentTraceObject();
+ if (trace == null) {
+ return;
+ }
+
+ try {
+ final SpanEventRecorder recorder = trace.currentSpanEventRecorder();
+ recorder.recordApi(this.descriptor);
+ recorder.recordServiceType(AgentSdkAsyncConstants.THREAD_POOL_EXECUTOR);
+ recorder.recordException(throwable);
+ } finally {
+ trace.traceBlockEnd();
+ }
+ }
+}
diff --git a/plugins/agentsdk-async/src/main/resources/META-INF/services/com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin b/plugins/agentsdk-async/src/main/resources/META-INF/services/com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin
new file mode 100644
index 0000000000000..65b88a04d7be5
--- /dev/null
+++ b/plugins/agentsdk-async/src/main/resources/META-INF/services/com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin
@@ -0,0 +1 @@
+com.navercorp.pinpoint.plugin.sdk.AgentSdkAsyncPlugin
diff --git a/plugins/agentsdk-async/src/main/resources/META-INF/services/com.navercorp.pinpoint.common.trace.TraceMetadataProvider b/plugins/agentsdk-async/src/main/resources/META-INF/services/com.navercorp.pinpoint.common.trace.TraceMetadataProvider
new file mode 100644
index 0000000000000..f738c758e93f7
--- /dev/null
+++ b/plugins/agentsdk-async/src/main/resources/META-INF/services/com.navercorp.pinpoint.common.trace.TraceMetadataProvider
@@ -0,0 +1 @@
+#com.navercorp.pinpoint.plugin.sdk.AgentSdkAsyncTraceMetadataProvider
\ No newline at end of file
diff --git a/plugins/assembly/pom.xml b/plugins/assembly/pom.xml
index aab90598439c5..0897f71289dbd 100644
--- a/plugins/assembly/pom.xml
+++ b/plugins/assembly/pom.xml
@@ -367,6 +367,12 @@
${project.version}
+
+ com.navercorp.pinpoint
+ pinpoint-agentsdk-async-plugin
+ ${project.version}
+
+
com.navercorp.pinpoint
pinpoint-process-plugin
diff --git a/plugins/pom.xml b/plugins/pom.xml
index 425778d3267f8..62fa5c26ec49b 100644
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -106,6 +106,7 @@
process
paho-mqtt
rocketmq
+ agentsdk-async
diff --git a/pom.xml b/pom.xml
index 78051804e6678..91ca6a10692bc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -69,6 +69,7 @@
testcase
batch
report
+ agent-sdk
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClass.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClass.java
index 8d7343b1be3d6..7cf9050365a41 100644
--- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClass.java
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClass.java
@@ -77,6 +77,9 @@ public ASMClass(EngineComponent engineComponent, final InstrumentContext pluginC
this.classNode = Objects.requireNonNull(classNode, "classNode");
}
+ public void test() {
+
+ }
public ClassLoader getClassLoader() {
return this.classNode.getClassLoader();
}
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/transformer/PinpointClassFilter.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/transformer/PinpointClassFilter.java
index 75462448ca566..21ce977d07a4c 100644
--- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/transformer/PinpointClassFilter.java
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/transformer/PinpointClassFilter.java
@@ -17,13 +17,26 @@
package com.navercorp.pinpoint.profiler.transformer;
import java.security.ProtectionDomain;
+import java.util.Objects;
/**
* @author emeroad
*/
public class PinpointClassFilter implements ClassFileFilter {
+ private static final String DEFAULT_PACKAGE = "com/navercorp/pinpoint/";
+ private static final String[] DEFAULT_EXCLUDES = {"web/", "sdk/"};
+
+ private final String basePackage;
+ private final String[] excludes;
+
public PinpointClassFilter() {
+ this(DEFAULT_PACKAGE, DEFAULT_EXCLUDES);
+ }
+
+ public PinpointClassFilter(String basePackage, String[] excludes) {
+ this.basePackage = Objects.requireNonNull(basePackage, "basePackage");
+ this.excludes = Objects.requireNonNull(excludes, "excludes");
}
@Override
@@ -33,9 +46,11 @@ public boolean accept(ClassLoader classLoader, String className, Class> classB
}
// Skip pinpoint packages too.
- if (className.startsWith("com/navercorp/pinpoint/")) {
- if (className.startsWith("com/navercorp/pinpoint/web/")) {
- return CONTINUE;
+ if (className.startsWith(basePackage)) {
+ for (String exclude : excludes) {
+ if (className.startsWith(exclude, basePackage.length())) {
+ return CONTINUE;
+ }
}
return SKIP;
}
diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/transformer/PinpointClassFilterTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/transformer/PinpointClassFilterTest.java
index 38fdc292e6b92..bf806465ded58 100644
--- a/profiler/src/test/java/com/navercorp/pinpoint/profiler/transformer/PinpointClassFilterTest.java
+++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/transformer/PinpointClassFilterTest.java
@@ -16,30 +16,38 @@
package com.navercorp.pinpoint.profiler.transformer;
-import com.navercorp.pinpoint.common.util.ClassLoaderUtils;
import org.junit.Assert;
import org.junit.Test;
-import java.net.URL;
-import java.net.URLClassLoader;
-
/**
* @author Woonduk Kang(emeroad)
*/
public class PinpointClassFilterTest {
+
@Test
- public void testDoFilter_Package() throws Exception {
+ public void doFilter() throws Exception {
+
+ ClassFileFilter filter = new PinpointClassFilter();
+
+ Assert.assertEquals(ClassFileFilter.CONTINUE, filter.accept(null, "java/test", null, null, null));
+ Assert.assertEquals(ClassFileFilter.CONTINUE, filter.accept(null, "javax/test", null, null, null));
+ Assert.assertEquals(ClassFileFilter.CONTINUE, filter.accept(null, "test", null, null, null));
+ }
+ @Test
+ public void doFilter_Package() throws Exception {
- final URLClassLoader agentClassLoader = new URLClassLoader(new URL[0]);
ClassFileFilter filter = new PinpointClassFilter();
- final ClassLoader defaultClassLoader = ClassLoaderUtils.getDefaultClassLoader();
+ Assert.assertEquals(ClassFileFilter.SKIP, filter.accept(null, "com/navercorp/pinpoint/", null, null, null));
+ }
+
+ @Test
+ public void doFilter_Package_exclude() throws Exception {
+
+ ClassFileFilter filter = new PinpointClassFilter();
- Assert.assertSame("pinpoint", filter.accept(defaultClassLoader, "com/navercorp/pinpoint/", null, null, null), ClassFileFilter.SKIP);
+ Assert.assertEquals(ClassFileFilter.CONTINUE, filter.accept(null, "com/navercorp/pinpoint/web/", null, null, null));
- Assert.assertSame(filter.accept(defaultClassLoader, "java/test", null, null, null), ClassFileFilter.CONTINUE);
- Assert.assertSame(filter.accept(defaultClassLoader, "javax/test", null, null, null), ClassFileFilter.CONTINUE);
- Assert.assertSame(filter.accept(defaultClassLoader, "test", null, null, null), ClassFileFilter.CONTINUE);
}
-}
+}
\ No newline at end of file