diff --git a/instrumentation/java.completable-future-jdk8/README.md b/instrumentation/java.completable-future-jdk8/README.md deleted file mode 100644 index fda331723f..0000000000 --- a/instrumentation/java.completable-future-jdk8/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# java.completable-future-jdk8 - -This instrumentation weaves `java.util.concurrent.CompletableFuture` and `java.util.concurrent.CompletableFuture$Async` to -trace code execution across asynchronous boundaries. - -## How it works - -Some context on parallelism according to comments in the -[CompletableFuture source](http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/concurrent/CompletableFuture.java#l77): - -> * All async methods without an explicit Executor -> * argument are performed using the {@link ForkJoinPool#commonPool()} -> * (unless it does not support a parallelism level of at least two, in -> * which case, a new Thread is used). To simplify monitoring, -> * debugging, and tracking, all generated asynchronous tasks are -> * instances of the marker interface {@link -> * AsynchronousCompletionTask}. - -When `CompletableFuture.execAsync(Executor e, Async r)` is invoked, it "starts the given async task using the given executor, -unless the executor is `ForkJoinPool.commonPool` and it has been disabled, in which case starts a new thread." - -Instrumented code: - -```java - static void execAsync(Executor e, CompletableFuture_Instrumentation.Async r) { - if (noParallelism(e)) { - new Thread(new TokenAwareRunnable(r)).start(); - } else { - Executor tde = useTokenDelegateExecutor(e); - if (null != tde) { - tde.execute(r); - } - } - } -``` - -### Case 1: No Parallelism - -If there is no parallelism this instrumentation will initialize a new `Thread` with a `TokenAwareRunnable` that wraps the `CompletableFuture$Async` argument -passed to `execAsync`. The `TokenAwareRunnable` uses `TokenAndRefUtils` to get a `TokenAndRefCount`, if one exists, for the current `Thread`. Otherwise, it -creates a new `TokenAndRefCount`. - -The `TokenAndRefCount` stores a `Token` that can be used to link asynchronous `Threads` together and tracks the number of incoming references to the `Token`. -When `TokenAwareRunnable.run()` is invoked the stored `Token` is linked on the executing `Thread` and finally the `Token` is expired when `run()` completes, -allowing the `Transaction` to complete. - -### Case 2: Parallelism - -In this case a `TokenDelegateExecutor` is initialized and used to wrap the `Executor` argument that was passed to `execAsync`. When -`TokenDelegateExecutor.execute(Runnable runnable)` is invoked it will initialize and store a `TokenAwareRunnable` that wraps the `CompletableFuture$Async` -argument passed to `execAsync`. From this point on, the `TokenAwareRunnable` functions exactly as described in Case 1: No Parallelism. - -## Logging - -This instrumentation will produce entries such as the following when searching the logs for keywords `token info`: - -``` -2022-01-07T17:22:03,481-0800 [53655 270] com.newrelic FINEST: [Empty token]: token info TokenAwareRunnable token info set -2022-01-07T17:22:03,482-0800 [53655 270] com.newrelic FINEST: [Empty token]: token info Token info set in thread -2022-01-07T17:22:03,482-0800 [53655 270] com.newrelic FINEST: [Empty token]: token info Clearing token info from thread -``` - -## Testing - -See the following functional tests: `newrelic-java-agent/functional_test/src/test/java/test/newrelic/test/agent/CompletableFutureTest.java` \ No newline at end of file diff --git a/instrumentation/java.completable-future-jdk8/build.gradle b/instrumentation/java.completable-future-jdk8/build.gradle deleted file mode 100644 index 1f25baa32b..0000000000 --- a/instrumentation/java.completable-future-jdk8/build.gradle +++ /dev/null @@ -1,20 +0,0 @@ -dependencies { - implementation(project(":agent-bridge")) -} - -// This instrumentation module should not use the bootstrap classpath - - -jar { - manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.java.completable-future-jdk8' } -} - -verifyInstrumentation { - verifyClasspath = false // We don't want to verify classpath since these are JDK classes -} - -site { - title 'Java Completable futures' - type 'Other' - versionOverride '[8,)' -} diff --git a/instrumentation/java.completable-future-jdk8/src/main/java/java/util/concurrent/CompletableFuture_Instrumentation.java b/instrumentation/java.completable-future-jdk8/src/main/java/java/util/concurrent/CompletableFuture_Instrumentation.java deleted file mode 100644 index 500668883e..0000000000 --- a/instrumentation/java.completable-future-jdk8/src/main/java/java/util/concurrent/CompletableFuture_Instrumentation.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package java.util.concurrent; - -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.Weave; -import util.TokenAwareRunnable; -import util.TokenDelegateExecutor; - -@Weave(type = MatchType.ExactClass, originalName = "java.util.concurrent.CompletableFuture") -public class CompletableFuture_Instrumentation { - - @Weave(type = MatchType.BaseClass, originalName = "java.util.concurrent.CompletableFuture$Async") - abstract static class Async extends ForkJoinTask - implements Runnable, CompletableFuture.AsynchronousCompletionTask { - public final Void getRawResult() { - return null; - } - - public final void setRawResult(Void v) { - } - - public final void run() { - exec(); - } - } - - private static boolean noParallelism(Executor e) { - return (e == ForkJoinPool.commonPool() && - ForkJoinPool.getCommonPoolParallelism() <= 1); - } - - private static Executor useTokenDelegateExecutor(Executor e) { - if (null == e || e instanceof TokenDelegateExecutor) { - return e; - } else { - return new TokenDelegateExecutor(e); - } - } - - static void execAsync(Executor e, CompletableFuture_Instrumentation.Async r) { - if (noParallelism(e)) { - new Thread(new TokenAwareRunnable(r)).start(); - } else { - Executor tde = useTokenDelegateExecutor(e); - if (null != tde) { - tde.execute(r); - } - } - } - -} diff --git a/instrumentation/java.completable-future-jdk8/src/main/java/util/TokenAndRefUtils.java b/instrumentation/java.completable-future-jdk8/src/main/java/util/TokenAndRefUtils.java deleted file mode 100644 index 0be21eb5e4..0000000000 --- a/instrumentation/java.completable-future-jdk8/src/main/java/util/TokenAndRefUtils.java +++ /dev/null @@ -1,63 +0,0 @@ -package util; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.agent.bridge.Transaction; - -import java.text.MessageFormat; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; - -public class TokenAndRefUtils { - - public static AgentBridge.TokenAndRefCount getThreadTokenAndRefCount() { - AgentBridge.TokenAndRefCount tokenAndRefCount = AgentBridge.activeToken.get(); - if (tokenAndRefCount == null) { - Transaction tx = AgentBridge.getAgent().getTransaction(false); - if (tx != null) { - tokenAndRefCount = new AgentBridge.TokenAndRefCount(tx.getToken(), - AgentBridge.getAgent().getTracedMethod(), new AtomicInteger(1)); - } - } else { - tokenAndRefCount.refCount.incrementAndGet(); - } - return tokenAndRefCount; - } - - public static Transaction getTransaction(AgentBridge.TokenAndRefCount tokenAndRefCount) { - if(tokenAndRefCount != null && tokenAndRefCount.token != null) { - return (Transaction) tokenAndRefCount.token.getTransaction(); - } else { - return null; - } - } - - public static void setThreadTokenAndRefCount(AgentBridge.TokenAndRefCount tokenAndRefCount, Transaction transaction) { - if (tokenAndRefCount != null && tokenAndRefCount.token != null) { - AgentBridge.activeToken.set(tokenAndRefCount); - tokenAndRefCount.token.link(); - } else if(tokenAndRefCount != null && transaction != null) { - tokenAndRefCount.token = transaction.getToken(); - tokenAndRefCount.token.link(); - tokenAndRefCount.refCount = new AtomicInteger(1); - } - } - - public static void clearThreadTokenAndRefCountAndTxn(AgentBridge.TokenAndRefCount tokenAndRefCount) { - AgentBridge.activeToken.remove(); - if (tokenAndRefCount != null && tokenAndRefCount.refCount.decrementAndGet() == 0) { - tokenAndRefCount.token.expire(); - tokenAndRefCount.token = null; - } - } - - public static void logTokenInfo(AgentBridge.TokenAndRefCount tokenAndRefCount, String msg) { - if (AgentBridge.getAgent().getLogger().isLoggable(Level.FINEST)) { - String tokenMsg = (tokenAndRefCount != null && tokenAndRefCount.token != null) - ? String.format("[%s:%s:%d]", tokenAndRefCount.token, tokenAndRefCount.token.getTransaction(), - tokenAndRefCount.refCount.get()) - : "[Empty token]"; - AgentBridge.getAgent().getLogger().log(Level.FINEST, MessageFormat.format("{0}: token info {1}", tokenMsg, msg)); - } - } - -} diff --git a/instrumentation/java.completable-future-jdk8/src/main/java/util/TokenAwareRunnable.java b/instrumentation/java.completable-future-jdk8/src/main/java/util/TokenAwareRunnable.java deleted file mode 100644 index 82b81afabd..0000000000 --- a/instrumentation/java.completable-future-jdk8/src/main/java/util/TokenAwareRunnable.java +++ /dev/null @@ -1,35 +0,0 @@ -package util; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.agent.bridge.Transaction; - -import static util.TokenAndRefUtils.*; - -public final class TokenAwareRunnable implements Runnable { - private final Runnable delegate; - - private AgentBridge.TokenAndRefCount tokenAndRefCount; - private Transaction transaction; - - public TokenAwareRunnable(Runnable delegate) { - this.delegate = delegate; - //get token state from calling Thread - this.tokenAndRefCount = getThreadTokenAndRefCount(); - this.transaction = getTransaction(tokenAndRefCount); - logTokenInfo(tokenAndRefCount, "TokenAwareRunnable token info set"); - } - - @Override - public void run() { - try { - if (delegate != null) { - logTokenInfo(tokenAndRefCount, "Token info set in thread"); - setThreadTokenAndRefCount(tokenAndRefCount, transaction); - delegate.run(); - } - } finally { - logTokenInfo(tokenAndRefCount, "Clearing token info from thread "); - clearThreadTokenAndRefCountAndTxn(tokenAndRefCount); - } - } -} diff --git a/instrumentation/java.completable-future-jdk8/src/main/java/util/TokenDelegateExecutor.java b/instrumentation/java.completable-future-jdk8/src/main/java/util/TokenDelegateExecutor.java deleted file mode 100644 index ab16289a1e..0000000000 --- a/instrumentation/java.completable-future-jdk8/src/main/java/util/TokenDelegateExecutor.java +++ /dev/null @@ -1,17 +0,0 @@ -package util; - -import java.util.concurrent.Executor; - -public class TokenDelegateExecutor implements Executor { - public final Executor delegate; - - public TokenDelegateExecutor(final Executor delegate) { - this.delegate = delegate; - } - - @Override - public void execute(Runnable runnable) { - runnable = new TokenAwareRunnable(runnable); - delegate.execute(runnable); - } -} diff --git a/settings.gradle b/settings.gradle index 05bc676a20..827e153235 100644 --- a/settings.gradle +++ b/settings.gradle @@ -141,7 +141,6 @@ include 'instrumentation:httpurlconnection' include 'instrumentation:hystrix-1.3.15' include 'instrumentation:hystrix-1.4' include 'instrumentation:jakarta.xml' -include 'instrumentation:java.completable-future-jdk8' include 'instrumentation:java.completable-future-jdk8u40' include 'instrumentation:java.logging-jdk8' include 'instrumentation:java-io'