diff --git a/instrumentation/spring/spring-core-2.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-core-2.0/javaagent/build.gradle.kts index e941d2620ad0..e012ab37e1f4 100644 --- a/instrumentation/spring/spring-core-2.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-core-2.0/javaagent/build.gradle.kts @@ -19,3 +19,10 @@ dependencies { // 4.0 introduces submitListenable() methods testLibrary("org.springframework:spring-core:4.0.0.RELEASE") } + +// spring 6 requires java 17 +if (findProperty("testLatestDeps") as Boolean) { + otelJava { + minJavaVersionSupported.set(JavaVersion.VERSION_17) + } +} diff --git a/instrumentation/spring/spring-core-2.0/javaagent/src/test/groovy/SimpleAsyncTaskExecutorInstrumentationTest.groovy b/instrumentation/spring/spring-core-2.0/javaagent/src/test/groovy/SimpleAsyncTaskExecutorInstrumentationTest.groovy deleted file mode 100644 index e90417f66851..000000000000 --- a/instrumentation/spring/spring-core-2.0/javaagent/src/test/groovy/SimpleAsyncTaskExecutorInstrumentationTest.groovy +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.api.GlobalOpenTelemetry -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import org.springframework.core.task.SimpleAsyncTaskExecutor -import spock.lang.Shared -import spock.lang.Unroll - -import java.util.concurrent.Callable -import java.util.concurrent.CountDownLatch - -import static io.opentelemetry.api.trace.SpanKind.INTERNAL - -class SimpleAsyncTaskExecutorInstrumentationTest extends AgentInstrumentationSpecification { - - @Shared - def executeRunnable = { e, c -> e.execute((Runnable) c) } - @Shared - def submitRunnable = { e, c -> e.submit((Runnable) c) } - @Shared - def submitCallable = { e, c -> e.submit((Callable) c) } - @Shared - def submitListenableRunnable = { e, c -> e.submitListenable((Runnable) c) } - @Shared - def submitListenableCallable = { e, c -> e.submitListenable((Callable) c) } - - @Unroll - def "should propagate context on #desc"() { - given: - def executor = new SimpleAsyncTaskExecutor() - - when: - runWithSpan("parent") { - def child1 = new AsyncTask(startSpan: true) - def child2 = new AsyncTask(startSpan: false) - method(executor, child1) - method(executor, child2) - child1.waitForCompletion() - child2.waitForCompletion() - } - - then: - assertTraces(1) { - trace(0, 2) { - span(0) { - name "parent" - kind INTERNAL - } - span(1) { - name "asyncChild" - kind INTERNAL - childOf(span(0)) - } - } - } - - where: - desc | method - "execute Runnable" | executeRunnable - "submit Runnable" | submitRunnable - "submit Callable" | submitCallable - "submitListenable Runnable" | submitListenableRunnable - "submitListenable Callable" | submitListenableCallable - } -} - -class AsyncTask implements Runnable, Callable { - private static final TRACER = GlobalOpenTelemetry.getTracer("test") - - final latch = new CountDownLatch(1) - boolean startSpan - - @Override - void run() { - if (startSpan) { - TRACER.spanBuilder("asyncChild").startSpan().end() - } - latch.countDown() - } - - @Override - Object call() throws Exception { - run() - return null - } - - void waitForCompletion() throws InterruptedException { - latch.await() - } -} diff --git a/instrumentation/spring/spring-core-2.0/javaagent/src/test/java/SimpleAsyncTaskExecutorInstrumentationTest.java b/instrumentation/spring/spring-core-2.0/javaagent/src/test/java/SimpleAsyncTaskExecutorInstrumentationTest.java new file mode 100644 index 000000000000..5e2baddaffd2 --- /dev/null +++ b/instrumentation/spring/spring-core-2.0/javaagent/src/test/java/SimpleAsyncTaskExecutorInstrumentationTest.java @@ -0,0 +1,111 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.function.ThrowingConsumer; +import org.springframework.core.task.SimpleAsyncTaskExecutor; + +public class SimpleAsyncTaskExecutorInstrumentationTest { + + @RegisterExtension + private static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + private static final SimpleAsyncTaskExecutor EXECUTOR = new SimpleAsyncTaskExecutor(); + + @Test + void executeRunnable() { + executeTwoTasks(EXECUTOR::execute); + } + + @Test + void submitRunnable() { + executeTwoTasks(task -> EXECUTOR.submit((Runnable) task)); + } + + @Test + void submitCallable() { + executeTwoTasks(task -> EXECUTOR.submit((Callable) task)); + } + + @Test + void submitListenableRunnable() { + executeTwoTasks(task -> EXECUTOR.submitListenable((Runnable) task)); + } + + @Test + void submitListenableCallable() { + executeTwoTasks(task -> EXECUTOR.submitListenable((Callable) task)); + } + + private static void executeTwoTasks(ThrowingConsumer task) { + testing.runWithSpan( + "parent", + () -> { + AsyncTask child1 = new AsyncTask(true); + AsyncTask child2 = new AsyncTask(false); + try { + task.accept(child1); + task.accept(child2); + } catch (Throwable throwable) { + throw new AssertionError(throwable); + } + child1.waitForCompletion(); + child2.waitForCompletion(); + }); + testing.waitAndAssertTraces( + trace -> + trace + .hasSize(2) + .hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName("asyncChild") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)))); + } + + static class AsyncTask implements Runnable, Callable { + + private static final Tracer TRACER = GlobalOpenTelemetry.getTracer("test"); + + private final boolean startSpan; + private final CountDownLatch latch = new CountDownLatch(1); + + public AsyncTask(boolean startSpan) { + this.startSpan = startSpan; + } + + @Override + public void run() { + if (startSpan) { + TRACER.spanBuilder("asyncChild").startSpan().end(); + } + latch.countDown(); + } + + @Override + public Object call() { + run(); + return null; + } + + void waitForCompletion() { + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new AssertionError(e); + } + } + } +}