From f62fac77c8cd8050e2034a5c65fab43d362252df Mon Sep 17 00:00:00 2001 From: jack-berg <34418638+jack-berg@users.noreply.github.com> Date: Tue, 2 Feb 2021 14:00:08 -0600 Subject: [PATCH 1/9] fix infinite tracing race condition regarding span observer --- .../java/com/newrelic/ChannelManager.java | 28 ++++++------- .../java/com/newrelic/ResponseObserver.java | 2 +- .../java/com/newrelic/ChannelManagerTest.java | 39 +++++++++---------- .../com/newrelic/ResponseObserverTest.java | 2 +- 4 files changed, 33 insertions(+), 38 deletions(-) diff --git a/infinite-tracing/src/main/java/com/newrelic/ChannelManager.java b/infinite-tracing/src/main/java/com/newrelic/ChannelManager.java index be2260d4e0..e8cbe132a1 100644 --- a/infinite-tracing/src/main/java/com/newrelic/ChannelManager.java +++ b/infinite-tracing/src/main/java/com/newrelic/ChannelManager.java @@ -28,8 +28,8 @@ class ChannelManager { @GuardedBy("lock") private boolean isShutdownForever; @GuardedBy("lock") private CountDownLatch backoffLatch; @GuardedBy("lock") private ManagedChannel managedChannel; + @GuardedBy("lock") private boolean recreateSpanObserver = true; @GuardedBy("lock") private ClientCallStreamObserver spanObserver; - @GuardedBy("lock") private ResponseObserver responseObserver; @GuardedBy("lock") private String agentRunToken; @GuardedBy("lock") private Map requestMetadata; @@ -87,12 +87,16 @@ ClientCallStreamObserver getSpanObserver() { logger.log(Level.FINE, "Creating gRPC channel."); managedChannel = buildChannel(); } - if (spanObserver == null) { - logger.log(Level.FINE, "Creating gRPC span observer."); + if (recreateSpanObserver) { + if (spanObserver != null) { + logger.log(Level.FINE, "Cancelling and recreating gRPC span observer."); + spanObserver.cancel("CLOSING_CONNECTION", new ChannelClosingException()); + } IngestServiceStub ingestServiceStub = buildStub(managedChannel); - responseObserver = buildResponseObserver(); + ResponseObserver responseObserver = buildResponseObserver(); spanObserver = (ClientCallStreamObserver) ingestServiceStub.recordSpan(responseObserver); aggregator.incrementCounter("Supportability/InfiniteTracing/Connect"); + recreateSpanObserver = false; } return spanObserver; } @@ -109,19 +113,11 @@ ResponseObserver buildResponseObserver() { } /** - * Cancel the span observer. The next time {@link #getSpanObserver()} is called the span observer - * will be recreated. This cancels the span observer with a {@link ChannelClosingException}, which - * {@link ResponseObserver#onError(Throwable)} detects and ignores. + * Mark that the span observer should be canceled and recreated the next time {@link #getSpanObserver()} is called. */ - void cancelSpanObserver() { + void recreateSpanObserver() { synchronized (lock) { - if (spanObserver == null) { - return; - } - logger.log(Level.FINE, "Canceling gRPC span observer."); - spanObserver.cancel("CLOSING_CONNECTION", new ChannelClosingException()); - spanObserver = null; - responseObserver = null; + recreateSpanObserver = true; } } @@ -146,7 +142,7 @@ void shutdownChannelAndBackoff(int backoffSeconds) { managedChannel.shutdown(); managedChannel = null; } - cancelSpanObserver(); + recreateSpanObserver(); } try { diff --git a/infinite-tracing/src/main/java/com/newrelic/ResponseObserver.java b/infinite-tracing/src/main/java/com/newrelic/ResponseObserver.java index bda63788ee..eb298520e5 100644 --- a/infinite-tracing/src/main/java/com/newrelic/ResponseObserver.java +++ b/infinite-tracing/src/main/java/com/newrelic/ResponseObserver.java @@ -111,7 +111,7 @@ private static boolean isConnectTimeoutError(Status status) { public void onCompleted() { logger.log(Level.FINE, "Completing gRPC response observer."); aggregator.incrementCounter("Supportability/InfiniteTracing/Response/Completed"); - channelManager.cancelSpanObserver(); + channelManager.recreateSpanObserver(); } } \ No newline at end of file diff --git a/infinite-tracing/src/test/java/com/newrelic/ChannelManagerTest.java b/infinite-tracing/src/test/java/com/newrelic/ChannelManagerTest.java index f043e3ad5b..746709f3bd 100644 --- a/infinite-tracing/src/test/java/com/newrelic/ChannelManagerTest.java +++ b/infinite-tracing/src/test/java/com/newrelic/ChannelManagerTest.java @@ -79,13 +79,28 @@ void getSpanObserver_BuildsChannelAndSpanObserverWhenMissing() { assertEquals(spanObserver, target.getSpanObserver()); assertEquals(spanObserver, target.getSpanObserver()); - verify(target, times(1)).buildChannel(); - verify(target, times(1)).buildStub(managedChannel); - verify(target, times(1)).buildResponseObserver(); + verify(target).buildChannel(); + verify(target).buildStub(managedChannel); + verify(target).buildResponseObserver(); verify(stub, times(1)).recordSpan(responseObserver); verify(aggregator).incrementCounter("Supportability/InfiniteTracing/Connect"); } + @Test + void getSpanObserver_RecreatesSpanObserver() { + assertEquals(spanObserver, target.getSpanObserver()); + target.recreateSpanObserver(); + assertEquals(spanObserver, target.getSpanObserver()); + assertEquals(spanObserver, target.getSpanObserver()); + + verify(target).buildChannel(); + verify(target, times(2)).buildStub(managedChannel); + verify(spanObserver).cancel(eq("CLOSING_CONNECTION"), any(ChannelClosingException.class)); + verify(target, times(2)).buildResponseObserver(); + verify(stub, times(2)).recordSpan(responseObserver); + verify(aggregator, times(2)).incrementCounter("Supportability/InfiniteTracing/Connect"); + } + @Test @Timeout(15) void getSpanObserver_AwaitsBackoff() throws ExecutionException, InterruptedException { @@ -126,29 +141,13 @@ public ClientCallStreamObserver call() { assertTrue(getSpanObserverCompletedAt.get() >= backoffCompletedAt.get()); } - @Test - void cancelSpanObserver_GetSpanObserverRebuildsWhenNextCalled() { - assertEquals(spanObserver, target.getSpanObserver()); - target.cancelSpanObserver(); - assertEquals(spanObserver, target.getSpanObserver()); - - verify(spanObserver).cancel(eq("CLOSING_CONNECTION"), any(ChannelClosingException.class)); - // Channel is only built once - verify(target, times(1)).buildChannel(); - // Span observer is built twice - verify(target, times(2)).buildStub(managedChannel); - verify(target, times(2)).buildResponseObserver(); - verify(stub, times(2)).recordSpan(responseObserver); - verify(aggregator, times(2)).incrementCounter("Supportability/InfiniteTracing/Connect"); - } - @Test void shutdownChannelAndBackoff_ShutsDownChannelCancelsSpanObserver() { assertEquals(spanObserver, target.getSpanObserver()); target.shutdownChannelAndBackoff(0); assertEquals(spanObserver, target.getSpanObserver()); - verify(target).cancelSpanObserver(); + verify(target).recreateSpanObserver(); // Channel and span observer is built twice verify(target, times(2)).buildChannel(); verify(target, times(2)).buildStub(managedChannel); diff --git a/infinite-tracing/src/test/java/com/newrelic/ResponseObserverTest.java b/infinite-tracing/src/test/java/com/newrelic/ResponseObserverTest.java index a06d694f0e..e56b70f640 100644 --- a/infinite-tracing/src/test/java/com/newrelic/ResponseObserverTest.java +++ b/infinite-tracing/src/test/java/com/newrelic/ResponseObserverTest.java @@ -129,7 +129,7 @@ void onCompleted_IncrementsCounterCancelsSpanObserver() { target.onCompleted(); verify(aggregator).incrementCounter("Supportability/InfiniteTracing/Response/Completed"); - verify(channelManager).cancelSpanObserver(); + verify(channelManager).recreateSpanObserver(); } } \ No newline at end of file From 0a1ea0150b171872731075f75d9f02ca615bbc2f Mon Sep 17 00:00:00 2001 From: Xi Xia Date: Tue, 2 Feb 2021 14:02:16 -0800 Subject: [PATCH 2/9] point release 6.4.1 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 6c2cb970b9..df341ea7a1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # The agent version. -agentVersion=6.4.0 +agentVersion=6.4.1 newrelicDebug=false org.gradle.jvmargs=-Xmx2048m From d63a60b5b4c749427af8168ec9953968e8dc442a Mon Sep 17 00:00:00 2001 From: Xi Xia Date: Fri, 26 Feb 2021 10:18:41 -0800 Subject: [PATCH 3/9] update version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index df341ea7a1..bb50f2d1ee 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # The agent version. -agentVersion=6.4.1 +agentVersion=6.4.2 newrelicDebug=false org.gradle.jvmargs=-Xmx2048m From 3303b313f99a7fa1d6d4ff3aac8f8369d055cb4b Mon Sep 17 00:00:00 2001 From: jasonjkeller Date: Thu, 1 Apr 2021 14:20:33 -0700 Subject: [PATCH 4/9] Add akka-http-2.13_2.4.5 inst module --- .../akka-http-2.13_2.4.5/build.gradle | 41 + .../scaladsl/marshalling/AkkaHttpMarshal.java | 29 + .../marshalling/AkkaHttpMarshallerMapper.java | 48 + .../AkkaHttpToResponseMarshallable.java | 29 + .../server/AkkaHttpContextFunction.scala | 68 + .../scaladsl/server/AkkaHttpPathMatchers.java | 74 + .../server/AkkaHttpRequestContext.scala | 55 + .../server/Directive_Instrumentation.java | 27 + ...hMatcherConstruction_Instrumentation.scala | 20 + .../NewRelicRequestContextWrapper.scala | 115 ++ .../server/PathMatcher_Instrumentation.scala | 66 + .../directives/AkkaExecutionDirectives.java | 32 + .../directives/OnSuccessMagnetDirective.java | 26 + .../akka/http/Function0Wrapper.java | 28 + .../akka/http/Function1Wrapper.java | 38 + .../akka/http/FutureWrapper.java | 156 ++ .../akka/http/InboundWrapper.scala | 35 + .../akka/http/OutboundWrapper.scala | 28 + .../akka/http/PathMatcherScalaUtils.scala | 98 + .../akka/http/PathMatcherUtils.java | 430 +++++ .../akka/http/RequestWrapper.scala | 62 + .../akka/http/ResponseWrapper.scala | 44 + .../akka/http/AkkaHttpRoutesTest.java | 1667 +++++++++++++++++ .../akka/http/AkkaResponseWrapperTest.java | 64 + .../akka/http/AkkaHttpTestRoutes.scala | 450 +++++ .../instrumentation/akka/http/AsyncApp.scala | 22 + .../akka/http/HttpServer.scala | 56 + .../akka/http/HttpServerRule.scala | 26 + .../akka/http/StatusCheckActor.scala | 43 + settings.gradle | 1 + 30 files changed, 3878 insertions(+) create mode 100644 instrumentation/akka-http-2.13_2.4.5/build.gradle create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala create mode 100644 instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/build.gradle b/instrumentation/akka-http-2.13_2.4.5/build.gradle new file mode 100644 index 0000000000..05f16e2233 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/build.gradle @@ -0,0 +1,41 @@ +apply plugin: 'scala' + +sourceSets.test.scala.srcDir "src/test/java" +sourceSets.test.java.srcDirs = [] + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.akka-http-2.13_2.4.5' } +} + +dependencies { + implementation(project(":agent-bridge")) + implementation("com.typesafe.akka:akka-http_2.13:10.1.8") + implementation("com.typesafe.akka:akka-stream_2.13:2.5.23") + implementation("com.typesafe.akka:akka-actor_2.13:2.5.23") + + testImplementation(project(":instrumentation:akka-2.2")) { transitive = false } + testImplementation(project(":instrumentation:scala-2.13.0")) { transitive = false } + testImplementation("com.jayway.restassured:rest-assured:2.7.0") + testImplementation("javax.xml.bind:jaxb-api:2.3.0") +} + +verifyInstrumentation { + passesOnly('com.typesafe.akka:akka-http_2.13:[2.4.5,)') { + compile("com.typesafe.akka:akka-stream_2.13:2.5.23") + } + excludeRegex 'com.typesafe.akka:akka-http_2.13:.*(RC|M)[0-9]*$' + excludeRegex 'com.typesafe.akka:akka-http_2.13:.*-[0-9a-f]{8}$' +} + +site { + title 'Akka Http' + type 'Framework' +} + +test { + // our dependency on rest-assured precludes running the tests on java 1.7 due to + // akka http only being able to run on java 1.8 and above from this version onward + onlyIf { + !project.hasProperty("test7") + } +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java new file mode 100644 index 0000000000..803d405da7 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java @@ -0,0 +1,29 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package akka.http.scaladsl.marshalling; + +import akka.http.scaladsl.model.HttpRequest; +import akka.http.scaladsl.model.HttpResponse; +import com.agent.instrumentation.akka.http.PathMatcherUtils; +import com.agent.instrumentation.akka.http.RequestWrapper; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import scala.concurrent.ExecutionContext; +import scala.concurrent.Future; + +@Weave(originalName = "akka.http.scaladsl.marshalling.Marshal") +public class AkkaHttpMarshal { + + public Future toResponseFor(HttpRequest request, Marshaller m, ExecutionContext ec) { + NewRelic.getAgent().getTransaction().setWebRequest(new RequestWrapper(request)); + PathMatcherUtils.reset(); + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java new file mode 100644 index 0000000000..ed02659532 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java @@ -0,0 +1,48 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package akka.http.scaladsl.marshalling; + +import akka.http.scaladsl.model.HttpResponse; +import com.agent.instrumentation.akka.http.ResponseWrapper; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.Transaction; +import com.newrelic.api.agent.weaver.Weaver; +import scala.runtime.AbstractFunction1; + +public class AkkaHttpMarshallerMapper extends AbstractFunction1 { + + private final Token token; + + public AkkaHttpMarshallerMapper(Token token) { + this.token = token; + } + + @Override + @Trace(async = true) + public HttpResponse apply(HttpResponse httpResponse) { + try { + if (token != null) { + token.linkAndExpire(); + } + ResponseWrapper responseWrapper = new ResponseWrapper(httpResponse); + Transaction transaction = NewRelic.getAgent().getTransaction(); + transaction.setWebResponse(responseWrapper); + transaction.addOutboundResponseHeaders(); + transaction.markResponseSent(); + + return responseWrapper.response(); + } catch (Throwable t) { + AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle()); + return httpResponse; + } + } + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java new file mode 100644 index 0000000000..4ab157cc5f --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java @@ -0,0 +1,29 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package akka.http.scaladsl.marshalling; + +import akka.http.scaladsl.model.HttpResponse; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(type = MatchType.Interface, originalName = "akka.http.scaladsl.marshalling.ToResponseMarshallable") +public abstract class AkkaHttpToResponseMarshallable { + + @NewField + public Token token; + + public Marshaller marshaller() { + Marshaller marshaller = Weaver.callOriginal(); + AkkaHttpMarshallerMapper akkaHttpMarshallerMapper = new AkkaHttpMarshallerMapper(token); + return marshaller.map(akkaHttpMarshallerMapper); + } + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala new file mode 100644 index 0000000000..7420fff142 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala @@ -0,0 +1,68 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package akka.http.scaladsl.server + +import com.agent.instrumentation.akka.http.PathMatcherUtils +import com.newrelic.agent.bridge.AgentBridge +import com.newrelic.api.agent.Trace + +import java.util.concurrent.LinkedBlockingDeque +import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger} +import java.util.logging.Level +import scala.collection.mutable +import scala.concurrent.Future +import scala.runtime.AbstractFunction1 + +object AkkaHttpContextFunction { + + final val retransformed = new AtomicBoolean(false) + + def contextWrapper(original: Function1[RequestContext, Future[RouteResult]]): Function1[RequestContext, Future[RouteResult]] = { + if (retransformed.compareAndSet(false, true)) { + AgentBridge.getAgent.getLogger.log(Level.FINER, "Retransforming akka.http.scaladsl.server.AkkaHttpContextFunction") + AgentBridge.instrumentation.retransformUninstrumentedClass(classOf[ContextWrapper]) + AgentBridge.getAgent.getLogger.log(Level.FINER, "Retransformed akka.http.scaladsl.server.AkkaHttpContextFunction") + } + + new ContextWrapper(original) + } + +} + +class ContextWrapper(original: Function1[RequestContext, Future[RouteResult]]) extends AbstractFunction1[RequestContext, Future[RouteResult]] { + + @Trace(dispatcher = true) + override def apply(ctx: RequestContext): Future[RouteResult] = { + try { + val tracedMethod = AgentBridge.getAgent.getTracedMethod + tracedMethod.setMetricName("AkkaHttp") + // Akka-http 10.1.5 uses CallbackRunnable and we lose transaction context between Directives + AgentBridge.getAgent.getTracedMethod.setTrackCallbackRunnable(true); + val token = AgentBridge.getAgent.getTransaction(false).getToken + PathMatcherUtils.setHttpRequest(ctx.request) + // We use this method to wire up our RequestContext wrapper and start our transaction + val newCtx = new NewRelicRequestContextWrapper(ctx, ctx.asInstanceOf[RequestContextImpl], token, + new LinkedBlockingDeque[String], new AtomicBoolean(false), new AtomicInteger(0), new AtomicInteger(0), + new LinkedBlockingDeque[String], new mutable.HashSet[String], ctx.request, ctx.unmatchedPath, ctx.executionContext, ctx.materializer, + ctx.log, ctx.settings, ctx.parserSettings) + original.apply(newCtx) + } catch { + case t: Throwable => { + AgentBridge.instrumentation.noticeInstrumentationError(t, "akka-http-2.4.5") + original.apply(ctx) + } + } + } + + override def compose[A](g: (A) => RequestContext): (A) => Future[RouteResult] = original.compose(g) + + override def andThen[A](g: (Future[RouteResult]) => A): (RequestContext) => A = original.andThen(g) + + override def toString(): String = original.toString() + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java new file mode 100644 index 0000000000..ce9cb0bb84 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java @@ -0,0 +1,74 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package akka.http.scaladsl.server; + +import akka.http.scaladsl.model.Uri; +import com.agent.instrumentation.akka.http.PathMatcherUtils; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import scala.Tuple1; +import scala.runtime.BoxedUnit; + +@Weave(type = MatchType.ExactClass, originalName = "akka.http.scaladsl.server.PathMatchers") +public class AkkaHttpPathMatchers { + + @Weave(type = MatchType.ExactClass, originalName = "akka.http.scaladsl.server.PathMatchers$Slash$") + public static class AkkaHttpSlash$ { + + public PathMatcher.Matching apply(final Uri.Path path) { + PathMatcher.Matching matching = Weaver.callOriginal(); + PathMatcherUtils.appendSlash(path, matching); + return matching; + } + + } + + @Weave(type = MatchType.ExactClass, originalName = "akka.http.scaladsl.server.PathMatchers$Remaining$") + public static class AkkaHttpRemaining$ { + + public PathMatcher.Matched> apply(final Uri.Path path) { + PathMatcher.Matched> matched = Weaver.callOriginal(); + PathMatcherUtils.appendRemaining("Remaining", path, matched); + return matched; + } + + } + + @Weave(type = MatchType.ExactClass, originalName = "akka.http.scaladsl.server.PathMatchers$RemainingPath$") + public static class AkkaHttpRemainingPath$ { + + public PathMatcher.Matched> apply(final Uri.Path path) { + PathMatcher.Matched> matched = Weaver.callOriginal(); + PathMatcherUtils.appendRemaining("RemainingPath", path, matched); + return matched; + } + + } + + @Weave(type = MatchType.BaseClass, originalName = "akka.http.scaladsl.server.PathMatchers$NumberMatcher") + public static class AkkaHttpNumberMatcher { + + public PathMatcher.Matching> apply(final Uri.Path path) { + PathMatcher.Matching> matching = Weaver.callOriginal(); + PathMatcherUtils.appendNumberMatch(getClass().getSimpleName().replaceAll("\\$", ""), path, matching); + return matching; + } + + } + + @Weave(type = MatchType.ExactClass, originalName = "akka.http.scaladsl.server.PathMatchers$Segment$") + public static class AkkaHttpSegment$ { + + public PathMatcher.Matching> apply(final Uri.Path path) { + PathMatcher.Matching> matching = Weaver.callOriginal(); + PathMatcherUtils.appendSegment(path, matching); + return matching; + } + } +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala new file mode 100644 index 0000000000..c337f675c8 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala @@ -0,0 +1,55 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package akka.http.scaladsl.server + +import akka.event.LoggingAdapter +import akka.http.scaladsl.marshalling.AkkaHttpToResponseMarshallable +import akka.http.scaladsl.model._ +import akka.http.scaladsl.settings.{ParserSettings, RoutingSettings} +import akka.stream.Materializer +import com.agent.instrumentation.akka.http.PathMatcherUtils +import com.newrelic.api.agent.weaver.{Weave, Weaver} + +import java.util.concurrent.LinkedBlockingDeque +import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger} +import scala.collection.mutable +import scala.concurrent.{ExecutionContextExecutor, Future} + +@Weave(originalName = "akka.http.scaladsl.server.RequestContextImpl") +abstract class AkkaHttpRequestContext(request: HttpRequest, + unmatchedPath: Uri.Path, + executionContext: ExecutionContextExecutor, + materializer: Materializer, + log: LoggingAdapter, + settings: RoutingSettings, + parserSettings: ParserSettings) { + + def complete(trm: AkkaHttpToResponseMarshallable): Future[RouteResult] = { + val contextWrapper = PathMatcherUtils.nrRequestContext.get() + if (trm != null && contextWrapper != null) { + trm.token = contextWrapper.token + } + Weaver.callOriginal() // This ends up calling complete on our NewRelicRequestContextWrapper + } + + def reconfigure(executionContext: ExecutionContextExecutor, materializer: Materializer, log: LoggingAdapter, settings: RoutingSettings): RequestContext = { + Weaver.callOriginal() + } + + private def copy(request: HttpRequest, + unmatchedPath: Uri.Path, + executionContext: ExecutionContextExecutor, + materializer: Materializer, + log: LoggingAdapter, + settings: RoutingSettings, + parserSettings: ParserSettings): RequestContextImpl = { + return new NewRelicRequestContextWrapper(this, Weaver.callOriginal(), null, new LinkedBlockingDeque[String](), + new AtomicBoolean(false), new AtomicInteger(0), new AtomicInteger(0), new LinkedBlockingDeque[String], new mutable.HashSet[String], request, + unmatchedPath, executionContext, materializer, log, settings, parserSettings) + } +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java new file mode 100644 index 0000000000..c561ef2894 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java @@ -0,0 +1,27 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package akka.http.scaladsl.server; + +import akka.http.scaladsl.server.util.Tuple; +import com.agent.instrumentation.akka.http.PathMatcherUtils; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import scala.Function1; +import scala.concurrent.Future; + +@Weave(type = MatchType.BaseClass, originalName = "akka.http.scaladsl.server.Directive$") +public abstract class Directive_Instrumentation { + + public Directive apply(Function1>>, + Function1>> function1, Tuple tuple) { + // Wrap any Directives that we see in order to propagate our request context properly + return new PathMatcherUtils.DirectiveWrapper<>(tuple, (Directive) Weaver.callOriginal()); + } + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala new file mode 100644 index 0000000000..0415d00f9d --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala @@ -0,0 +1,20 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package akka.http.scaladsl.server + +import com.agent.instrumentation.akka.http.PathMatcherScalaUtils +import com.newrelic.api.agent.weaver.{MatchType, Weave, Weaver} + +import scala.util.matching.Regex + +@Weave(`type` = MatchType.BaseClass, originalName = "akka.http.scaladsl.server.ImplicitPathMatcherConstruction") +abstract class ImplicitPathMatcherConstruction_Instrumentation { + implicit def _regex2PathMatcher(regex: Regex): PathMatcher1[String] = { + PathMatcherScalaUtils.pathMatcherWrapper(PathMatcherScalaUtils.emptyFunction1(), PathMatcherScalaUtils.appendRegex(regex), Weaver.callOriginal()) + } +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala new file mode 100644 index 0000000000..ba8db4b50a --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala @@ -0,0 +1,115 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package akka.http.scaladsl.server + +import akka.event.LoggingAdapter +import akka.http.scaladsl.marshalling.ToResponseMarshallable +import akka.http.scaladsl.model.{HttpRequest, Uri} +import akka.http.scaladsl.settings.{ParserSettings, RoutingSettings} +import akka.stream.Materializer +import com.agent.instrumentation.akka.http.PathMatcherUtils +import com.newrelic.agent.bridge.{AgentBridge, Token} +import com.newrelic.api.agent.{Trace, TransactionNamePriority} + +import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger} +import scala.collection.mutable +import scala.concurrent.{ExecutionContextExecutor, Future} + +class NewRelicRequestContextWrapper(originalRequestContext: Object, + underlyingRequestContext: RequestContextImpl, + var token: Token, + var matchedPath: java.util.Deque[String], + var divertRepeat: AtomicBoolean, + var currentMatchedQueueLength: AtomicInteger, + var previousMatchedQueueLength: AtomicInteger, + var repeatHolder: java.util.Deque[String], + var regexHolder: mutable.Set[String], + request: HttpRequest, + unmatchedPath: Uri.Path, + executionContext: ExecutionContextExecutor, + materializer: Materializer, + log: LoggingAdapter, + settings: RoutingSettings, + parserSettings: ParserSettings) + extends RequestContextImpl(request, unmatchedPath, executionContext, materializer, log, settings, parserSettings) { + PathMatcherUtils.nrRequestContext.set(this) + originalRequestContext match { + case wrapper: NewRelicRequestContextWrapper => + token(wrapper.token) + matchedPath(wrapper.matchedPath) + divertRepeat(wrapper.divertRepeat) + currentMatchedQueueLength(wrapper.currentMatchedQueueLength) + previousMatchedQueueLength(wrapper.previousMatchedQueueLength) + repeatHolder(wrapper.repeatHolder) + regexHolder(wrapper.regexHolder) + case _ => + } + + override def reconfigure(executionContext: ExecutionContextExecutor, materializer: Materializer, log: LoggingAdapter, + settings: RoutingSettings): RequestContext = { + underlyingRequestContext.reconfigure(executionContext, materializer, log, settings) + } + + @Trace(async = true) + override def complete(trm: ToResponseMarshallable): Future[RouteResult] = { + try { + if (token != null) { + val transactionName = PathMatcherUtils.finishPathAndGetTransactionName(this) + token.getTransaction.setTransactionName(TransactionNamePriority.FRAMEWORK_HIGH, false, "AkkaHttp", transactionName) + token.link() + } + + underlyingRequestContext.complete(trm).map(result => { + completeResponse(token) + result + })(executionContext) + } catch { + case t: Throwable => AgentBridge.instrumentation.noticeInstrumentationError(t, "akka-http-2.4.5") + underlyingRequestContext.complete(trm) + } + } + + @Trace(async = true) + def completeResponse(token: Token): Unit = { + try { + if (token != null) { + token.linkAndExpire() + } + } catch { + case t: Throwable => AgentBridge.instrumentation.noticeInstrumentationError(t, "akka-http-2.4.5") + } + } + + def token(token: Token): Unit = { + this.token = token + } + + def matchedPath(matchedPath: java.util.Deque[String]): Unit = { + this.matchedPath = matchedPath + } + + def divertRepeat(divertRepeat: AtomicBoolean): Unit = { + this.divertRepeat = divertRepeat + } + + def currentMatchedQueueLength(currentMatchedQueueLength: AtomicInteger): Unit = { + this.currentMatchedQueueLength = currentMatchedQueueLength + } + + def previousMatchedQueueLength(previousMatchedQueueLength: AtomicInteger): Unit = { + this.previousMatchedQueueLength = previousMatchedQueueLength + } + + def repeatHolder(repeatHolder: java.util.Deque[String]): Unit = { + this.repeatHolder = repeatHolder + } + + def regexHolder(regexHolder: mutable.Set[String]): Unit = { + this.regexHolder = regexHolder + } +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala new file mode 100644 index 0000000000..27e8e408d6 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala @@ -0,0 +1,66 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package akka.http.scaladsl.server + +import akka.http.scaladsl.model.Uri.Path +import akka.http.scaladsl.server.PathMatcher.Matching +import akka.http.scaladsl.server.util.Tuple +import com.agent.instrumentation.akka.http.{PathMatcherScalaUtils, PathMatcherUtils} +import com.newrelic.api.agent.weaver.{MatchType, Weave, Weaver} + +@Weave(`type` = MatchType.BaseClass, originalName = "akka.http.scaladsl.server.PathMatcher") +abstract class PathMatcher_Instrumentation[L] { + + def ev: Tuple[L] = Weaver.callOriginal() + + def |[R >: L : Tuple](other: PathMatcher[_ <: R]): PathMatcher[R] = { + PathMatcherScalaUtils.pathMatcherWrapper(PathMatcherScalaUtils.appendPipe(), PathMatcherScalaUtils.emptyFunction2(), Weaver.callOriginal()) + } + + def unary_!(): PathMatcher0 = { + val result: PathMatcher0 = Weaver.callOriginal() + PathMatcherScalaUtils.pathMatcher0Wrapper(PathMatcherScalaUtils.appendNegation(), result) + } + + def repeat(min: Int, max: Int, separator: PathMatcher0 = PathMatchers.Neutral)(implicit lift: PathMatcher.Lift[L, List]): PathMatcher[lift.Out] = { + PathMatcherScalaUtils.pathMatcherWrapper(PathMatcherScalaUtils.startRepeat(), PathMatcherScalaUtils.endRepeat(), + Weaver.callOriginal())(lift.OutIsTuple) + } +} + +@Weave(`type` = MatchType.ExactClass, originalName = "akka.http.scaladsl.server.PathMatcher$") +abstract class PathMatcherObject_Instrumentation { + def apply[L](prefix: Path, extractions: L, evidence: Tuple[L]): PathMatcher[L] = { + PathMatcherScalaUtils.pathMatcherWrapper(PathMatcherScalaUtils.emptyFunction1(), PathMatcherScalaUtils.appendStaticString(prefix.toString()), + Weaver.callOriginal())(evidence) + } +} + +@Weave(`type` = MatchType.ExactClass, originalName = "akka.http.scaladsl.server.PathMatcher$EnhancedPathMatcher") +abstract class EnhancedPathMatcher[L](underlying: PathMatcher_Instrumentation[L]) { + def ?(implicit lift: PathMatcher.Lift[L, Option]): PathMatcher[lift.Out] = { + val result: PathMatcher[lift.Out] = Weaver.callOriginal() + PathMatcherScalaUtils.pathMatcherWrapper(PathMatcherScalaUtils.appendOptional(), PathMatcherScalaUtils.emptyFunction2(), result)(lift.OutIsTuple) + } +} + +@Weave(`type` = MatchType.ExactClass, originalName = "akka.http.scaladsl.server.PathMatcher$Matched") +class Matched_Instrumentation[L: Tuple](path: Path, extractions: L) { + + def pathRest: Path = { + Weaver.callOriginal() + } + + def andThen[R: Tuple](f: (Path, L) ⇒ Matching[R]): Matching[R] = { + PathMatcherUtils.appendTilde(null) + val returnValue = Weaver.callOriginal.asInstanceOf[Matching[R]] + PathMatcherUtils.andThen(returnValue, pathRest) + returnValue + } + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java new file mode 100644 index 0000000000..c0b70a898c --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package akka.http.scaladsl.server.directives; + +import akka.http.scaladsl.server.AkkaHttpContextFunction; +import akka.http.scaladsl.server.ExceptionHandler; +import akka.http.scaladsl.server.RejectionHandler; +import akka.http.scaladsl.server.RequestContext; +import akka.http.scaladsl.server.RouteResult; +import akka.http.scaladsl.settings.ParserSettings; +import akka.http.scaladsl.settings.RoutingSettings; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import scala.Function1; +import scala.concurrent.Future; + +@Weave(originalName = "akka.http.scaladsl.server.Route$") +public class AkkaExecutionDirectives { + + public Function1> seal(Function1> f1, + RoutingSettings routingSettings, ParserSettings parserSettings, RejectionHandler rejectionHandler, + ExceptionHandler exceptionHandler) { + Function1> result = Weaver.callOriginal(); + return AkkaHttpContextFunction.contextWrapper(result); + } + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java new file mode 100644 index 0000000000..ceb4dd88e1 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java @@ -0,0 +1,26 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package akka.http.scaladsl.server.directives; + +import akka.http.scaladsl.server.util.Tupler; +import com.agent.instrumentation.akka.http.Function0Wrapper; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import scala.Function0; +import scala.concurrent.Future; + +@Weave(type = MatchType.BaseClass, originalName = "akka.http.scaladsl.server.directives.OnSuccessMagnet$") +public class OnSuccessMagnetDirective { + + public OnSuccessMagnet apply(Function0> f, Tupler tupler) { + f = new Function0Wrapper(f); + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java new file mode 100644 index 0000000000..2c94715e75 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java @@ -0,0 +1,28 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http; + +import scala.Function0; +import scala.concurrent.Future; +import scala.runtime.AbstractFunction0; + +public class Function0Wrapper extends AbstractFunction0> { + + private final Function0> original; + + public Function0Wrapper(Function0> original) { + this.original = original; + } + + @Override + public Future apply() { + Future result = original.apply(); + return new FutureWrapper<>(result); + } + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java new file mode 100644 index 0000000000..a988867ee1 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java @@ -0,0 +1,38 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weaver; +import scala.Function1; +import scala.runtime.AbstractFunction1; + +public class Function1Wrapper extends AbstractFunction1 { + + private final Function1 original; + private final Token token; + + public Function1Wrapper(Function1 original, Token token) { + this.original = original; + this.token = token; + } + + @Override + @Trace(async = true) + public U apply(T v1) { + try { + token.linkAndExpire(); + } catch (Throwable t) { + AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle()); + } + return original.apply(v1); + } + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java new file mode 100644 index 0000000000..9a861372b9 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java @@ -0,0 +1,156 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weaver; +import scala.$less$colon$less; +import scala.Function1; +import scala.Function2; +import scala.Option; +import scala.PartialFunction; +import scala.Tuple2; +import scala.concurrent.Awaitable; +import scala.concurrent.CanAwait; +import scala.concurrent.ExecutionContext; +import scala.concurrent.Future; +import scala.concurrent.duration.Duration; +import scala.reflect.ClassTag; +import scala.util.Try; + +import java.util.concurrent.TimeoutException; + +public class FutureWrapper implements Future { + + private final Future original; + + public FutureWrapper(Future original) { + this.original = original; + } + + @Override + public void onComplete(Function1, U> f, ExecutionContext executor) { + try { + f = new Function1Wrapper<>(f, NewRelic.getAgent().getTransaction().getToken()); + } catch (Throwable t) { + AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle()); + } + original.onComplete(f, executor); + } + + @Override + public boolean isCompleted() { + return original.isCompleted(); + } + + @Override + public Option> value() { + return original.value(); + } + + @Override + public Future failed() { + return original.failed(); + } + + @Override + public void foreach(Function1 f, ExecutionContext executor) { + original.foreach(f, executor); + } + + @Override + public Future transform(Function1 s, Function1 f, ExecutionContext executor) { + return original.transform(s, f, executor); + } + + @Override + public Future transform(Function1, Try> f, ExecutionContext executor) { + return original.transform(f, executor); + } + + @Override + public Future transformWith(Function1, Future> f, ExecutionContext executor) { + return original.transformWith(f, executor); + } + + @Override + public Future map(Function1 f, ExecutionContext executor) { + return original.map(f, executor); + } + + @Override + public Future flatMap(Function1> f, ExecutionContext executor) { + return original.flatMap(f, executor); + } + + @Override + public Future flatten($less$colon$less> ev) { + return original.flatten(ev); + } + + @Override + public Future filter(Function1 p, ExecutionContext executor) { + return original.filter(p, executor); + } + + @Override + public Future withFilter(Function1 p, ExecutionContext executor) { + return original.withFilter(p, executor); + } + + @Override + public Future collect(PartialFunction pf, ExecutionContext executor) { + return original.collect(pf, executor); + } + + @Override + public Future recover(PartialFunction pf, ExecutionContext executor) { + return original.recover(pf, executor); + } + + @Override + public Future recoverWith(PartialFunction> pf, ExecutionContext executor) { + return original.recoverWith(pf, executor); + } + + @Override + public Future> zip(Future that) { + return original.zip(that); + } + + @Override + public Future zipWith(Future that, Function2 f, ExecutionContext executor) { + return original.zipWith(that, f, executor); + } + + @Override + public Future fallbackTo(Future that) { + return original.fallbackTo(that); + } + + @Override + public Future mapTo(ClassTag tag) { + return original.mapTo(tag); + } + + @Override + public Future andThen(PartialFunction, U> pf, ExecutionContext executor) { + return original.andThen(pf, executor); + } + + @Override + public Awaitable ready(Duration atMost, CanAwait permit) throws InterruptedException, TimeoutException { + return original.ready(atMost, permit); + } + + @Override + public T result(Duration atMost, CanAwait permit) throws Exception { + return original.result(atMost, permit); + } +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala new file mode 100644 index 0000000000..19aae129b0 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala @@ -0,0 +1,35 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http + +import akka.http.scaladsl.model.HttpRequest +import com.newrelic.api.agent.{ExtendedInboundHeaders, HeaderType} + +import java.util +import scala.jdk.javaapi.CollectionConverters + +class InboundWrapper(request: HttpRequest) extends ExtendedInboundHeaders { + + def getHeaderType: HeaderType = { + HeaderType.HTTP + } + + def getHeader(name: String): String = { + request.headers.find(header => header.is(name.toLowerCase)).map(header => header.value).orNull + } + + override def getHeaders(name: String): util.List[String] = { + val headers = request.headers.filter(header => header.is(name.toLowerCase)).map(header => header.value) + if (headers.isEmpty) { + return null + } + CollectionConverters.asJava(headers) + } + +} + diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala new file mode 100644 index 0000000000..9318a0a4ff --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala @@ -0,0 +1,28 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http + +import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.model.headers.RawHeader +import com.newrelic.api.agent.{HeaderType, OutboundHeaders} + +class OutboundWrapper(var response: HttpResponse) extends OutboundHeaders { + + def getHeaderType: HeaderType = { + HeaderType.HTTP + } + + def setHeader(name: String, value: String): Unit = { + response = response.addHeader(new RawHeader(name, value)) + } + + def getHeader(name: String): String = { + response.headers.find(header => header.is(name.toLowerCase)).map(header => header.value).orNull + } + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala new file mode 100644 index 0000000000..58e992ed92 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala @@ -0,0 +1,98 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http + +import akka.http.scaladsl.model.Uri.Path +import akka.http.scaladsl.server.util.Tuple +import akka.http.scaladsl.server.{PathMatcher, PathMatcher0} +import com.newrelic.agent.bridge.AgentBridge +import com.newrelic.api.agent.weaver.Weaver + +import scala.util.matching.Regex + +object PathMatcherScalaUtils { + + def pathMatcher0Wrapper(runBefore: Path => Unit, original: PathMatcher0): PathMatcher0 = { + new PathMatcher[Unit] { + def apply(path: Path): PathMatcher.Matching[Unit] = { + try { + runBefore.apply(path) + } catch { + case t: Throwable => AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle) + } + original.apply(path) + } + } + } + + def pathMatcherWrapper[L](runBefore: Path => Unit, runAfter: (Path, PathMatcher.Matching[L]) => Unit, original: PathMatcher[L])(implicit ev: Tuple[L]): PathMatcher[L] = { + new PathMatcher[L] { + override def apply(path: Path): PathMatcher.Matching[L] = { + try { + runBefore.apply(path) + } catch { + case t: Throwable => AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle) + } + val result = original.apply(path) + try { + runAfter.apply(path, result) + } catch { + case t: Throwable => AgentBridge.instrumentation.noticeInstrumentationError(t, Weaver.getImplementationTitle) + } + result + } + } + } + + def appendNegation(): Path => Unit = { + (path: Path) => PathMatcherUtils.appendNegation() + } + + def appendOptional(): Path => Unit = { + (path: Path) => PathMatcherUtils.appendOptional() + } + + def appendPipe(): Path => Unit = { + (path: Path) => PathMatcherUtils.appendPipe(path) + } + + def appendRegex[L](regex: Regex): (Path, PathMatcher.Matching[L]) => Unit = { + + (path: Path, matching: PathMatcher.Matching[L]) => { + val nrRequestContext = PathMatcherUtils.nrRequestContext.get() + if (matching.isInstanceOf[PathMatcher.Matched[L]] && nrRequestContext != null && !nrRequestContext.regexHolder.contains(regex.toString())) { + PathMatcherUtils.appendRegex(path, regex.pattern.toString, matching) + nrRequestContext.regexHolder.add(regex.toString()) + } + } + } + + def startRepeat(): Path => Unit = { + (path: Path) => PathMatcherUtils.startRepeat(path) + } + + def endRepeat[L](): (Path, PathMatcher.Matching[L]) => Unit = { + (path: Path, matching: PathMatcher.Matching[L]) => { + PathMatcherUtils.endRepeat(path, matching) + } + } + + def appendStaticString[L](prefix: String): (Path, PathMatcher.Matching[L]) => Unit = { + (path: Path, matching: PathMatcher.Matching[L]) => { + PathMatcherUtils.appendStaticString(path, prefix, matching) + } + } + + def emptyFunction1(): Path => Unit = { + (path: Path) => () + } + + def emptyFunction2[L](): (Path, PathMatcher.Matching[L]) => Unit = { + (path: Path, pathMatcher: PathMatcher.Matching[L]) => () + } +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java new file mode 100644 index 0000000000..c3b5df50ba --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java @@ -0,0 +1,430 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http; + +import akka.http.scaladsl.model.HttpRequest; +import akka.http.scaladsl.model.Uri; +import akka.http.scaladsl.server.Directive; +import akka.http.scaladsl.server.NewRelicRequestContextWrapper; +import akka.http.scaladsl.server.PathMatcher; +import akka.http.scaladsl.server.RequestContext; +import akka.http.scaladsl.server.RequestContextImpl; +import akka.http.scaladsl.server.RouteResult; +import akka.http.scaladsl.server.util.Tuple; +import com.newrelic.agent.bridge.AgentBridge; +import scala.Function1; +import scala.concurrent.Future; +import scala.runtime.AbstractFunction1; + +import java.util.Deque; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.logging.Level; + +public class PathMatcherUtils { + + /** + * The purpose of this initializer is to hook into a place that's only called once during initialization of the + * akka-http library so we can work around an issue where our agent fails to transform the RequestContext class. + */ + static { + AgentBridge.getAgent().getLogger().log(Level.FINER, "Retransforming akka.http.scaladsl.server.RequestContextImpl"); + AgentBridge.instrumentation.retransformUninstrumentedClass(RequestContextImpl.class); + AgentBridge.getAgent().getLogger().log(Level.FINER, "Retransformed akka.http.scaladsl.server.RequestContextImpl"); + AgentBridge.getAgent().getLogger().log(Level.FINER, "Retransforming akka.http.scaladsl.server.NewRelicRequestContextWrapper"); + AgentBridge.instrumentation.retransformUninstrumentedClass(NewRelicRequestContextWrapper.class); + AgentBridge.getAgent().getLogger().log(Level.FINER, "Retransformed akka.http.scaladsl.server.NewRelicRequestContextWrapper"); + } + + public static final Class matchedClass = PathMatcher.Matched.class; + public static final Class unmatchedClass = PathMatcher.Unmatched$.class; + + // This allows us to get access to the request context (where the assembled path information is stored) + public static final ThreadLocal nrRequestContext = + new ThreadLocal() { + @Override + protected NewRelicRequestContextWrapper initialValue() { + return null; + } + }; + + /** + * When we have a match on a portion of the path, this method appends one of the numeric types to the path. This + * helps to prevent a metric explosion by replacing potentially dynamic values with static strings. + * + * @param numberMatchType the numeric path type ("IntNumber", "HexIntNumber", etc) + * @param path the current path match to append to + * @param matching the type of match (Matched or Unmatched) + */ + public static void appendNumberMatch(String numberMatchType, Uri.Path path, PathMatcher.Matching matching) { + if (matching.getClass().isAssignableFrom(matchedClass)) { + insertPathValue(numberMatchType); + } else if (matching.getClass().isAssignableFrom(unmatchedClass)) { + handleUnmatched(path, numberMatchType); + } + } + + /** + * Add any "Segment" matches to the path. + * + * @param path the current path match to append to + * @param matching the type of match (Matched or Unmatched) + */ + public static void appendSegment(Uri.Path path, PathMatcher.Matching matching) { + if (matching.getClass().isAssignableFrom(matchedClass)) { + insertPathValue("Segment"); + } else if (matching.getClass().isAssignableFrom(unmatchedClass)) { + handleUnmatched(path, "Segment"); + } + } + + /** + * Mark the start of a repeating pattern + */ + public static void startRepeat(Uri.Path path) { + NewRelicRequestContextWrapper ctx = nrRequestContext.get(); + if (ctx != null && !ctx.divertRepeat().get()) { + ctx.divertRepeat().set(true); + + Deque pathQueue = ctx.matchedPath(); + pathQueue.offer("("); + } + } + + /** + * Mark the end of the repeating pattern by copying the temporary "repeatHolder" pattern into the main + * matched path. A repeat pattern will look something like this: + *

+ * "(IntNumber/).repeat()" + * + * @param path the current path match to append to + * @param matching the type of match (Matched or Unmatched) + */ + public static void endRepeat(Uri.Path path, PathMatcher.Matching matching) { + final int MAX_APPENDED_SEGMENTS_TO_INCLUDE = 2; + + NewRelicRequestContextWrapper ctx = nrRequestContext.get(); + if (ctx == null) { + return; + } + ctx.divertRepeat().set(false); + + if (matching instanceof PathMatcher.Matched) { + PathMatcher.Matched> matched = (PathMatcher.Matched) matching; + Deque repeatPathQueue = ctx.repeatHolder(); + Deque pathQueue = ctx.matchedPath(); + for (int i = 0; i < MAX_APPENDED_SEGMENTS_TO_INCLUDE; i++) { + String queueResult = repeatPathQueue.pollFirst(); + if (queueResult != null) { + pathQueue.add(queueResult); + } + } + pathQueue.offer(").repeat()"); + } + + ctx.repeatHolder(new LinkedBlockingDeque()); + } + + /** + * Add any static string matches to the path. This will be something like "/foo" or "/bar" + * + * @param path the current path match to append to + * @param prefix the matched static prefix (hardcoded string) + * @param matching the type of match (Matched or Unmatched) + */ + public static void appendStaticString(Uri.Path path, String prefix, PathMatcher.Matching matching) { + if (matching.getClass().isAssignableFrom(matchedClass)) { + insertPathValue(prefix); + } else if (matching.getClass().isAssignableFrom(unmatchedClass)) { + handleUnmatched(path, prefix); + } + } + + /** + * Add any regex matches to the path + * + * @param path the current path match to append to + * @param regexPattern the matched regex mattern + * @param matching the type of match (Matched or Unmatched) + */ + public static void appendRegex(Uri.Path path, String regexPattern, PathMatcher.Matching matching) { + if (matching.getClass().isAssignableFrom(matchedClass)) { + insertPathValue(regexPattern); + } else if (matching.getClass().isAssignableFrom(unmatchedClass)) { + handleUnmatched(path, regexPattern); + } + } + + /** + * Adds a negation ("!") to the path + */ + public static void appendNegation() { + insertPathValue("!"); + } + + /** + * Adds a tilde ("~") to the path which represents a concatenation. There are a couple of special cases here for ignoring + * the tilde if it's the first part of the path, if the previous item added was a slash or if we are in the middle of a repeat. + * This helps clear up the final path value and get it as close to what the user entered as possible. + * + * @param path the current path match to append to + */ + public static void appendTilde(Uri.Path path) { + Deque pathQueue = getPathQueue(); + + NewRelicRequestContextWrapper ctx = nrRequestContext.get(); + if (pathQueue.isEmpty() && ctx != null && ctx.divertRepeat().get()) { + // We are in the middle of a repeating segment, we don't want these to show up as tildes (~) + return; + } + + if (pathQueue.isEmpty() || !pathQueue.peekLast().equals("/")) { + insertPathValue("~"); + } + } + + /** + * Adds a pipe ("|") to the path + * + * @param path the current path match to append to + */ + public static void appendPipe(Uri.Path path) { + insertPathValue("|"); + } + + /** + * Adds an optional ("?") to the path + */ + public static void appendOptional() { + insertPathValue("?"); + } + + /** + * Adds a Slash ("/") to the path + * + * @param path the current path match to append to + */ + public static void appendSlash(Uri.Path path, PathMatcher.Matching matching) { + if (matching.getClass().isAssignableFrom(matchedClass)) { + Deque pathQueue = getPathQueue(); + + // Special case to help clean up extra tildes + if (!pathQueue.isEmpty() && pathQueue.peekLast().equals("~")) { + pathQueue.removeLast(); + } + insertPathValue("/"); + } else if (!path.isEmpty() && matching.getClass().isAssignableFrom(unmatchedClass)) { + handleUnmatched(path, "/"); + } + } + + /** + * Adds a "Remaining" or "RemainingPath" to the end of the path to correspond to that matcher. + * + * @param type the type of "Remaining" match + * @param path the current path match to append to + * @param matched the type of match (Matched) + */ + public static void appendRemaining(String type, Uri.Path path, PathMatcher.Matched matched) { + insertPathValue(type); + } + + /** + * This is used when a specific path partially matches and we have more to check via a tilde concatenation. If the + * secondary match fails we need to handle the unmatch and clear out the path. + * + * @param matching the type of match. + */ + public static void andThen(PathMatcher.Matching matching, Uri.Path pathRest) { + if (matching != null && matching.getClass().isAssignableFrom(unmatchedClass)) { + handleUnmatched(pathRest, null); + } else { + NewRelicRequestContextWrapper ctx = nrRequestContext.get(); + if (ctx != null) { + // We had a successful Match, record the current queue length on the context + ctx.currentMatchedQueueLength().set(getPathQueue().size()); + } + } + } + + /** + * Finishes the current path by gathering up all of the stored values in the Deque and building up a string + * to use as a the transaction name. + * + * @param ctx the wrapped RequestContext holding the path information + * @return the transaction name for this path + */ + public static String finishPathAndGetTransactionName(NewRelicRequestContextWrapper ctx) { + Deque pathElements = ctx.matchedPath(); + + StringBuilder finalPath = new StringBuilder(); + if (pathElements == null) { + finalPath.append("Unknown Route"); + } else { + // First, do some cleanup + if (!pathElements.isEmpty() && pathElements.peekLast().equals("~")) { + pathElements.removeLast(); + } + if (!pathElements.isEmpty() && !pathElements.peekFirst().equals("/")) { + pathElements.addFirst("/"); + } + + for (String pathElement; (pathElement = pathElements.poll()) != null; ) { + // More cleanup + if (pathElement.equals("|")) { + continue; + } + + finalPath.append(pathElement); + } + + pathElements.clear(); + } + + String finalPathString = finalPath.toString(); + return finalPathString.isEmpty() ? "Unknown Route" : finalPathString; + } + + /** + * Handles inserting the new path value into the Deque as well as special-case logic for optionals + * + * @param pathValue the value to insert + */ + private static void insertPathValue(String pathValue) { + Deque pathQueue = getPathQueue(); + if (!pathQueue.isEmpty() && pathQueue.peekLast().equals("!")) { + // The result during a "negation" matched, which means this shouldn't match + pathQueue.clear(); + return; + } + + boolean previousOptional = !pathQueue.isEmpty() && pathQueue.peekLast().equals("?"); + if (previousOptional) { + pathQueue.removeLast(); + } + pathQueue.offer(pathValue); + if (previousOptional) { + pathQueue.offer(".?"); + } + } + + private static Deque getPathQueue() { + NewRelicRequestContextWrapper ctx = nrRequestContext.get(); + if (ctx != null) { + if (ctx.divertRepeat().get()) { + return ctx.repeatHolder(); + } + return ctx.matchedPath(); + } + return new LinkedBlockingDeque<>(); + } + + /** + * Since we came across an "unmatched" that means we'll need to erase our previous progress as it didn't match + * anything. The one special case here is if the previous operator was a negation ("!") and in that case we + * want to continue attempting to match. + * + * @param path the current path that failed to match + * @param prefix in the case of a negation this is the value following it (the value to be negated) + */ + private static void handleUnmatched(Uri.Path path, String prefix) { + NewRelicRequestContextWrapper ctx = nrRequestContext.get(); + if (ctx != null) { + Deque pathQueue = getPathQueue(); + if (!pathQueue.isEmpty()) { + if (pathQueue.peekLast().equals("!")) { + pathQueue.offer(prefix); // !{prefix} + } else if (pathQueue.peekLast().equals("|")) { + // Pipe ("|") here means that the first match failed, but the second might + // not so we want to remove the pipe marker and let the match continue + pathQueue.removeLast(); + } else if (pathQueue.peekLast().equals("?")) { + pathQueue.removeLast(); + pathQueue.offer(prefix); + pathQueue.offer(".?"); + } else if (pathQueue.peekLast().equals("~") && !path.isEmpty()) { + // If we got here, it means that we matched something and it is a concatenation so we may have paths + // to remove in order to get back to a last known "matching" state. + int expectedQueueSize = ctx.currentMatchedQueueLength().get() + 1; // Include the tilde (~) + + // If the expected queue size is the same as the current path, they it means we have paths to remove + // so we need to set the current matched queue length back to the previous known match. Otherwise, + // we can move forward by setting the previous size equal to the new current size + if (expectedQueueSize == getPathQueue().size()) { + ctx.currentMatchedQueueLength().set(ctx.previousMatchedQueueLength().get()); + } else { + ctx.previousMatchedQueueLength().set(ctx.currentMatchedQueueLength().get()); + } + + for (int i = getPathQueue().size(); i > expectedQueueSize - 1; i--) { + pathQueue.removeLast(); + } + + } else if (prefix == null) { + for (int i = getPathQueue().size(); i > ctx.previousMatchedQueueLength().get(); i--) { + pathQueue.removeLast(); + } + } else if (ctx.divertRepeat().get()) { + // If we are in the middle of a repeat match, an "unmatched" is not an issue + return; + } else { + int matchedQueueSize = ctx.currentMatchedQueueLength().get(); + int previousQueueSize = ctx.previousMatchedQueueLength().get(); + int expectedQueueSize = matchedQueueSize + 1; // Includes the trailing tilde or slash + if (matchedQueueSize > 0 && previousQueueSize == 0 && pathQueue.size() == expectedQueueSize) { + // This case is here to handle where we've matched a pathPrefix, a sub path has failed but we have more paths we want to check. + // If we just cleared out the queue here we would either get an UnknownRoute or an incorrect route. Setting the + // previousMatchedQueueLength here ensures that we keep the pathPrefix for additional checks + ctx.previousMatchedQueueLength().set(ctx.currentMatchedQueueLength().get()); + } else { + pathQueue.clear(); + ctx.regexHolder().clear(); + } + } + } else { + pathQueue.clear(); + ctx.regexHolder().clear(); + } + } + } + + public static void reset() { + nrRequestContext.remove(); + } + + public static void setHttpRequest(HttpRequest request) { + AgentBridge.getAgent().getTransaction().setWebRequest(new RequestWrapper(request)); + } + + public static class DirectiveWrapper extends Directive { + + private final Directive underlying; + + public DirectiveWrapper(Tuple ev, Directive underlying) { + super(ev); + this.underlying = underlying; + + // Remove the current request context since we may be switching threads in this directive + nrRequestContext.remove(); + } + + @Override + public Function1> tapply(Function1>> f) { + Function1> result = underlying.tapply(f); + return result.compose(new AbstractFunction1() { + @Override + public RequestContext apply(RequestContext requestContext) { + if (requestContext instanceof NewRelicRequestContextWrapper) { + // If we have a New Relic wrapped RequestContext we should set this back into the thread local so we can use it after this directive + nrRequestContext.set(((NewRelicRequestContextWrapper) requestContext)); + } + return requestContext; + } + }); + } + } +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala new file mode 100644 index 0000000000..46b105f1ec --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala @@ -0,0 +1,62 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http + +import akka.http.scaladsl.model.HttpRequest +import com.newrelic.api.agent.{ExtendedRequest, HeaderType} + +import java.util +import scala.jdk.javaapi.CollectionConverters + +class RequestWrapper(request: HttpRequest) extends ExtendedRequest { + + def getMethod: String = { + request.method.name + } + + def getRequestURI: String = { + request.uri.path.toString() + } + + def getRemoteUser: String = { + null + } + + def getParameterNames: util.Enumeration[_] = { + CollectionConverters.asJavaEnumeration(request.uri.query().toMap.keysIterator) + } + + def getParameterValues(name: String): Array[String] = { + request.uri.query().getAll(name).toArray + } + + def getAttribute(name: String): AnyRef = { + null + } + + def getCookieValue(name: String): String = { + request.cookies.find(cookie => cookie.name.equalsIgnoreCase(name)).map(cookie => cookie.value).orNull + } + + def getHeaderType: HeaderType = { + HeaderType.HTTP + } + + def getHeader(name: String): String = { + request.headers.find(header => header.is(name.toLowerCase)).map(header => header.value).orNull + } + + override def getHeaders(name: String): util.List[String] = { + val headers = request.headers.filter(header => header.is(name.toLowerCase)).map(header => header.value) + if (headers.isEmpty) { + return null + } + CollectionConverters.asJava(headers) + } + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala new file mode 100644 index 0000000000..a67da2b3ad --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala @@ -0,0 +1,44 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http + +import akka.http.scaladsl.model.HttpResponse +import akka.http.scaladsl.model.headers.RawHeader +import com.newrelic.api.agent.{ExtendedResponse, HeaderType} + +class ResponseWrapper(var response: HttpResponse) extends ExtendedResponse { + + def getStatus: Int = { + response.status.intValue + } + + def getStatusMessage: String = { + response.status.reason + } + + def getContentType: String = { + response.entity.contentType.value + } + + def getHeaderType: HeaderType = { + HeaderType.HTTP + } + + def setHeader(name: String, value: String): Unit = { + response = response.addHeader(new RawHeader(name, value)) + } + + def getContentLength: Long = { + val contentLength = response.getHeader("Content-Length") + if (contentLength.isPresent) { + return contentLength.get().value().toLong + } + response.entity.getContentLengthOption().orElse(-1L) + } + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java b/instrumentation/akka-http-2.13_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java new file mode 100644 index 0000000000..9f5683b1d2 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java @@ -0,0 +1,1667 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http; + +import com.jayway.restassured.response.Headers; +import com.jayway.restassured.response.ValidatableResponse; +import com.newrelic.agent.HeadersUtil; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.agent.util.Obfuscator; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.UnsupportedEncodingException; +import java.util.Collection; +import java.util.UUID; + +import static com.jayway.restassured.RestAssured.given; +import static org.hamcrest.Matchers.containsString; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "akka", "scala", "com.agent", "com.nr" }) +public class AkkaHttpRoutesTest { + + private static final long TIMEOUT = 30000; + + @Rule + public HttpServerRule server = HttpServerRule$.MODULE$.apply(InstrumentationTestRunner.getIntrospector().getRandomPort(), + new AkkaHttpTestRoutes().routes()); + + @Test + public void testHostAndPort() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/hostandport") + .then() + .body(containsString("OK")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), introspector.getTransactionNames().contains(getTransactionPrefix() + "/hostandport")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testFutureErrorStatusCode() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/test-error") + .then() + .body(containsString("There was an internal server error.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), introspector.getTransactionNames().contains(getTransactionPrefix() + "/test-error")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "500"); + } + + @Test + public void testFutureError2StatusCode() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/test-error-2") + .then() + .body(containsString("ErrorTest")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), introspector.getTransactionNames().contains(getTransactionPrefix() + "/test-error-2")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "500"); + } + + @Test + public void testPrefixFirst() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/prefix-first") + .then() + .body(containsString("prefix-first")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), introspector.getTransactionNames().contains(getTransactionPrefix() + "/prefix-first")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testPrefixFirstFuture() { + given().baseUri("http://localhost:" + server.getPort()).when() + .get("/prefix-first-future") + .then() + .body(containsString("prefix-first-future")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/prefix-first-future")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testPrefixFirstSecond() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/prefix-first-second") + .then() + .body(containsString("prefix-first-second")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/prefix-first-second")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testPrefixFirstSecondFuture() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/prefix-first-second-future") + .then() + .body(containsString("prefix-first-second-future")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/prefix-first-second-future")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testSimpleRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/simple/route") + .then() + .body(containsString("Simple Route")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/simple/route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testSimpleRouteFuture() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/simple/route/future") + .then() + .body(containsString("Simple Route Future")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/simple/route/future")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testSimpleRouteWithQueryParam() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/simple/route?query=value") + .then() + .body(containsString("Simple Route")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/simple/route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testSimpleRouteFutureWithQueryParam() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/simple/route/future?query=value") + .then() + .body(containsString("Simple Route Future")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/simple/route/future")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testSimpleRouteCAT() throws UnsupportedEncodingException { + String idHeader = Obfuscator.obfuscateNameUsingKey("1xyz234#1xyz3333", "cafebabedeadbeef8675309babecafe1beefdead"); + + ValidatableResponse response = given() + .header(HeadersUtil.NEWRELIC_ID_HEADER, idHeader) + .baseUri("http://localhost:" + server.getPort()).when() + .get("/simple/route?query=value") + .then() + .body(containsString("Simple Route")); + + Headers responseHeaders = response.extract().headers(); + Assert.assertTrue(responseHeaders.hasHeaderWithName(HeadersUtil.NEWRELIC_APP_DATA_HEADER)); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/simple/route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testSimpleRouteFutureCAT() throws UnsupportedEncodingException { + String idHeader = Obfuscator.obfuscateNameUsingKey("1xyz234#1xyz3333", "cafebabedeadbeef8675309babecafe1beefdead"); + + ValidatableResponse response = given() + .header(HeadersUtil.NEWRELIC_ID_HEADER, idHeader) + .baseUri("http://localhost:" + server.getPort()).when() + .get("/simple/route/future?query=value") + .then() + .body(containsString("Simple Route Future")); + + Headers responseHeaders = response.extract().headers(); + Assert.assertTrue(responseHeaders.hasHeaderWithName(HeadersUtil.NEWRELIC_APP_DATA_HEADER)); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/simple/route/future")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testUUIDRoute() { + String uuidRegex = "[\\da-fA-F]{8}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{12}"; + UUID uuid = UUID.randomUUID(); + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/uuid/" + uuid.toString()) + .then() + .body(containsString("UUID: " + uuid.toString())); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/uuid/" + uuidRegex)); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testUUIDFutureRoute() { + String uuidRegex = "[\\da-fA-F]{8}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{12}"; + UUID uuid = UUID.randomUUID(); + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/uuid/future/" + uuid.toString()) + .then() + .body(containsString("UUID Future: " + uuid.toString())); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/uuid/future/" + uuidRegex)); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testRegexRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/regex/5") + .then() + .body(containsString("Regex: 5")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/regex/\\d+")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testRegexFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/regex/future/5") + .then() + .body(containsString("Regex Future: 5")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/regex/future/\\d+")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testNoMatchRegexRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/regex/a") + .then() + .body(containsString("The requested resource could not be found.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testNoMatchFutureRegexRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/regex/future/a") + .then() + .body(containsString("The requested resource could not be found.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testMapRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/map/red") + .then() + .body(containsString("Map: 1")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/map/red")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testMapFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/map/future/red") + .then() + .body(containsString("Map Future: 1")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/map/future/red")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternateMapRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/map/blue") + .then() + .body(containsString("Map: 3")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/map/blue")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternateMapFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/map/future/blue") + .then() + .body(containsString("Map Future: 3")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/map/future/blue")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testSegmentRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/segment/foobar") + .then() + .body(containsString("Segment: bar")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/segment/foo~Segment")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testSegmentFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/segment/future/foobar") + .then() + .body(containsString("Segment Future: bar")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/segment/future/foo~Segment")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternateSegmentRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/segment/food100") + .then() + .body(containsString("Segment: d100")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/segment/foo~Segment")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternateSegmentFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/segment/future/food100") + .then() + .body(containsString("Segment Future: d100")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/segment/future/foo~Segment")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testUnmatchedSegmentRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/segment") + .then() + .body(containsString("The requested resource could not be found.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testUnmatchedSegmentFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/segment/future") + .then() + .body(containsString("The requested resource could not be found.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testAlternateUnmatchedSegmentRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/segment/foo") + .then() + .body(containsString("The requested resource could not be found.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testAlternateUnmatchedSegmentFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/segment/future/foo") + .then() + .body(containsString("The requested resource could not be found.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testSecondaryAlternateUnmatchedSegmentRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/segment/foobar/baz") + .then() + .body(containsString("The requested resource could not be found.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testSecondaryAlternateUnmatchedSegmentFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/segment/future/foobar/baz") + .then() + .body(containsString("The requested resource could not be found.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testPathEndRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/pathend") + .then() + .body(containsString("PathEnd")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/pathend")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testPathEndFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/pathendfuture") + .then() + .body(containsString("PathEndFuture")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/pathendfuture")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testNonPathEndRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/pathend/extra") + .then() + .body(containsString("PathEnd: extra")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/pathend/Segment")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testNonPathEndFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/pathendfuture/extra") + .then() + .body(containsString("PathEndFuture: extra")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/pathendfuture/Segment")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testRemainingPath() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/remaining") + .then() + .body(containsString("Remain: ing")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/remain~Remaining")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testRemainingFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/futureremaining") + .then() + .body(containsString("FutureRemain: ing")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/futureremain~Remaining")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternateRemainingRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/remaining/this/is/the/remaining") + .then() + .body(containsString("Remain: ing/this/is/the/remaining")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/remain~Remaining")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternateRestFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/futureremaining/this/is/the/remaining") + .then() + .body(containsString("FutureRemain: ing/this/is/the/remaining")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/futureremain~Remaining")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testSecondaryAlternateRestRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/remaining") + .then() + .body(containsString("Remain: ing")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/remain~Remaining")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testSecondaryAlternateRemainingFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/futureremaining") + .then() + .body(containsString("FutureRemain: ing")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/futureremain~Remaining")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testRemainingPathRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/remainingpath/remaining") + .then() + .body(containsString("RemainingPath: remaining")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/remainingpath/RemainingPath")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testRemainingPathFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/futureremainingpath/remaining") + .then() + .body(containsString("FutureRemainingPath: remaining")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/futureremainingpath/RemainingPath")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testIntNumberRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/int/10") + .then() + .body(containsString("IntNumber: 10")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/int/IntNumber")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testIntNumberFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/intfuture/10") + .then() + .body(containsString("IntNumberFuture: 10")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/intfuture/IntNumber")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testLongNumberRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/long/1337") + .then() + .body(containsString("LongNumber: 1337")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/long/LongNumber")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testLongNumberFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/longfuture/1337") + .then() + .body(containsString("LongNumberFuture: 1337")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/longfuture/LongNumber")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testHexIntNumberRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/hexint/CAFE") + .then() + .body(containsString("HexIntNumber: 51966")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/hexint/HexIntNumber")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testHexIntNumberFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/hexintfuture/CAFE") + .then() + .body(containsString("HexIntNumberFuture: 51966")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/hexintfuture/HexIntNumber")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testHexLongNumberRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/hexlong/CAFE") + .then() + .body(containsString("HexLongNumber: 51966")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/hexlong/HexLongNumber")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testHexLongNumberFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/hexlongfuture/CAFE") + .then() + .body(containsString("HexLongNumberFuture: 51966")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/hexlongfuture/HexLongNumber")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testDoubleNumberRoute() { + String doubleRegex = "[+-]?\\d*\\.?\\d*"; + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/double/123.456") + .then() + .body(containsString("DoubleNumber: 123.456")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/double/" + doubleRegex)); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testDoubleNumberFutureRoute() { + String doubleRegex = "[+-]?\\d*\\.?\\d*"; + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/double/future/123.456") + .then() + .body(containsString("DoubleNumberFuture: 123.456")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/double/future/" + doubleRegex)); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testSegmentsRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/segments/here/are/segments") + .then() + .body(containsString("Segments: here,are,segments")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/segments/(Segment/).repeat()")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testSegmentsFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/futuresegments/here/are/segments") + .then() + .body(containsString("FutureSegments: here,are,segments")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), introspector.getTransactionNames().contains( + getTransactionPrefix() + "/futuresegments/(Segment/).repeat()")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testUnmatchedRepeatRouteTooFewItems() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/repeat/52/complex") + .then() + .body(containsString("The requested resource could not be found.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testUnmatchedRepeatRouteTooManyItems() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/repeat/52/53/54/55/complex") + .then() + .body(containsString("The requested resource could not be found.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testComplexRepeatRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/repeat/52/53/54/complex") + .then() + .body(containsString("Repeat: 52,53,54")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), introspector.getTransactionNames().contains( + getTransactionPrefix() + "/repeat/(IntNumber/).repeat()/complex")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testZeroRepeatRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/zerorepeat/") + .then() + .body(containsString("ZeroRepeat:")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), introspector.getTransactionNames().contains( + getTransactionPrefix() + "/zerorepeat/().repeat()")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testComplexRepeatFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/futurerepeat/52/53/complex") + .then() + .body(containsString("FutureRepeat: 52,53")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), introspector.getTransactionNames().contains( + getTransactionPrefix() + "/futurerepeat/(IntNumber/).repeat()/complex")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternativeComplexRepeatRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/repeat/52/53/54/complex") + .then() + .body(containsString("Repeat: 52,53,54")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), introspector.getTransactionNames().contains( + getTransactionPrefix() + "/repeat/(IntNumber/).repeat()/complex")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternativeComplexRepeatFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/futurerepeat/52/53/54/complex") + .then() + .body(containsString("FutureRepeat: 52,53,54")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), introspector.getTransactionNames().contains( + getTransactionPrefix() + "/futurerepeat/(IntNumber/).repeat()/complex")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testPipeRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/pipe/i5") + .then() + .body(containsString("Pipe: 5")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/pipe/i~IntNumber")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testFuturePipeRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/futurepipe/i5") + .then() + .body(containsString("FuturePipe: 5")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/futurepipe/i~IntNumber")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternativePipeRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/pipe/hCAFE") + .then() + .body(containsString("Pipe: 51966")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/pipe/h~HexIntNumber")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternativePipeFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/futurepipe/hCAFE") + .then() + .body(containsString("FuturePipe: 51966")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/futurepipe/h~HexIntNumber")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testPipeOptionalRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/pipe/optional/X/create") + .then() + .body(containsString("Pipe + Optional: null")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/pipe/optional/X~IntNumber.?/create")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testPipeOptionalFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/futurepipe/optional/X/create") + .then() + .body(containsString("FuturePipe + Optional: null")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/futurepipe/optional/X~IntNumber.?/create")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternativePipeOptionalRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/pipe/optional/X71/edit") + .then() + .body(containsString("Pipe + Optional: 71")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/pipe/optional/X~IntNumber.?/edit")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternativePipeOptionalFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/futurepipe/optional/X71/edit") + .then() + .body(containsString("FuturePipe + Optional: 71")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/futurepipe/optional/X~IntNumber.?/edit")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testNegationRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/match") + .then() + .body(containsString("Negation")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/match~!nomatch")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testNegationFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/futurematch") + .then() + .body(containsString("FutureNegation")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/futurematch~!nomatch")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternativeNegationRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/matchno") + .then() + .body(containsString("Negation")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/match~!nomatch")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAlternativeNegationFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/futurematchno") + .then() + .body(containsString("FutureNegation")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/futurematch~!nomatch")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testUnmatchedNegationRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/matchnomatch") + .then() + .body(containsString("The requested resource could not be found.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testUnmatchedNegationFutureRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/futurematchnomatch") + .then() + .body(containsString("The requested resource could not be found.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testSinglePrefix() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/v1") + .then() + .body(containsString("The requested resource could not be found.")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testDoublePrefixNoParam() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/v1/containers") + .then() + .body(containsString("Request is missing required query parameter 'parameter'")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/Unknown Route")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "404"); + } + + @Test + public void testDoublePrefixWithParam() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/v1/containers?parameter=12345") + .then() + .body(containsString("ContainersParam: 12345")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/v1/containers")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testDoublePrefixWithSegment() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/v1/containers/DT12345") + .then() + .body(containsString("ContainersSegment: DT12345")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/v1/containers/Segment")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testDoublePrefixWithSegmentAndString() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/v1/containers/DT12345/details") + .then() + .body(containsString("ContainersSegmentDetails: DT12345")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/v1/containers/Segment/details")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testDoublePrefixWithSegmentAndStrings() { + given() + .baseUri("http://localhost:" + server.getPort()).when().get("/v1/containers/DT12345/details/test") + .then() + .body(containsString("ContainersSegmentDetailsTest: DT12345")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/v1/containers/Segment/details/test")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testCompleteWithFuture() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/future/2000") + .then() + .body(containsString("OK")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/future/IntNumber")); + Collection transactionEvents = introspector.getTransactionEvents(getTransactionPrefix() + "/future/IntNumber"); + TransactionEvent transactionEvent = (TransactionEvent) transactionEvents.toArray()[0]; + Assert.assertNotNull(transactionEvent); + Assert.assertTrue(transactionEvent.getDurationInSec() >= 2.0); + + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testCustomAsyncDirectiveNoResult() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/custom-directive/v2/docs?parameter=test") + .then() + .body(containsString("CustomDirectiveDocsParam: test")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/custom-directive/v2/docs")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + + } + + @Test + public void testCustomAsyncDirectiveWithResult() { + for (int i = 0; i < 100; i++) { + given() + .baseUri("http://localhost:" + server.getPort()) + .header("X-NewRelic-Directive", "booyah!") + .when() + .get("/custom-directive/v2/docs/booyah") + .then() + .body(containsString("CustomDirectiveDocsSegment: booyah")); + } + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(100, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertEquals(1, introspector.getTransactionNames().size()); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/custom-directive/v2/docs/Segment")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 100, "200"); + + } + + @Test + public void testPathEndRoute2() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/path-end") + .then() + .body(containsString("Get path end!")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/path-end")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testPathEndRemainingRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/path-prefix-end") + .then() + .body(containsString("Get path end!")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/path-prefix-end")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testPathEndRemainingRoute2() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/path-prefix-end/first-case") + .then() + .body(containsString("First case")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/path-prefix-end/first-case")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testPathEndRemainingRoute3() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/path-prefix-end/whatever") + .then() + .body(containsString("Remaining: whatever")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/path-prefix-end/Remaining")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + } + + @Test + public void testAsyncDirectiveRoute() { + given() + .baseUri("http://localhost:" + server.getPort()).when() + .get("/callid/102") + .then() + .body(containsString("Pong_OK")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + Assert.assertTrue(introspector.getTransactionNames().toString(), + introspector.getTransactionNames().contains(getTransactionPrefix() + "/callid/Segment")); + + Collection transactionEvents = introspector.getTransactionEvents(introspector.getTransactionNames().toArray()[0].toString()); + assertResponseCodeOnTxEvents(transactionEvents, 1, "200"); + assertCustomAttributeOnTxEvents(transactionEvents, "attr1"); + assertCustomAttributeOnTxEvents(transactionEvents, "attr2"); + } + + private void assertResponseCodeOnTxEvents(Collection transactionEvents, int expectedSize, String expectedResponseCode) { + Assert.assertNotNull(transactionEvents); + Assert.assertEquals(expectedSize, transactionEvents.size()); + for (TransactionEvent transactionEvent : transactionEvents) { + String httpResponseCode = String.valueOf(transactionEvent.getAttributes().get("httpResponseCode")); + Assert.assertNotNull(httpResponseCode); + Assert.assertEquals(expectedResponseCode, httpResponseCode); + } + } + + private void assertCustomAttributeOnTxEvents(Collection transactionEvents, String expectedAttributeKey) { + Assert.assertNotNull(transactionEvents); + for (TransactionEvent transactionEvent : transactionEvents) { + String attributeValue = String.valueOf(transactionEvent.getAttributes().get(expectedAttributeKey)); + Assert.assertNotNull(attributeValue); + } + } + + private String getTransactionPrefix() { + return "WebTransaction/AkkaHttp"; + } + +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java b/instrumentation/akka-http-2.13_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java new file mode 100644 index 0000000000..e9cb5a0816 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java @@ -0,0 +1,64 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Collection; + +import static com.jayway.restassured.RestAssured.given; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "akka", "scala" }) +public class AkkaResponseWrapperTest { + + private static final long TIMEOUT = 30000; + + @Rule + public HttpServerRule server = HttpServerRule$.MODULE$.apply(InstrumentationTestRunner.getIntrospector().getRandomPort(), + new AkkaHttpTestRoutes().routes()); + + @Test + public void testAttributesContainsContentType() { + given() + .baseUri("http://localhost:" + server.getPort()) + .when() + .get("/prefix-first") + .then() + .body(containsString("prefix-first")); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(TIMEOUT)); + assertTrue(introspector.getTransactionNames().toString(), introspector.getTransactionNames().contains(getTransactionPrefix() + "/prefix-first")); + + String txName = introspector.getTransactionNames().toArray()[0].toString(); + assertNotNull(txName); + Collection transactionEvents = introspector.getTransactionEvents(txName); + assertFalse(transactionEvents.isEmpty()); + TransactionEvent transactionEvent = (TransactionEvent) transactionEvents.toArray()[0]; + assertNotNull(transactionEvent); + assertTrue(transactionEvent.getAttributes().containsKey("response.headers.contentType")); + Object o = transactionEvent.getAttributes().get("response.headers.contentType"); + assertNotNull(o); + } + + private String getTransactionPrefix() { + return "WebTransaction/AkkaHttp"; + } +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala b/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala new file mode 100644 index 0000000000..1f7b39f4e6 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala @@ -0,0 +1,450 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http + +import akka.actor.{ActorRef, ActorSystem, Scheduler} +import akka.http.scaladsl.marshalling.ToResponseMarshallable +import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.model.headers.RawHeader +import akka.http.scaladsl.server.Directives._ +import akka.http.scaladsl.server._ +import akka.http.scaladsl.server.directives.BasicDirectives +import akka.pattern.{after, ask} +import akka.stream.ActorMaterializer +import akka.util.{ByteString, Timeout} +import com.agent.instrumentation.akka.http.StatusCheckActor.Ping +import com.newrelic.api.agent.NewRelic + +import scala.concurrent.duration._ +import scala.concurrent.{ExecutionContext, Future} +import scala.util.Success + +class AkkaHttpTestRoutes { + + implicit val system: ActorSystem = ActorSystem("akkahttptest") + implicit val scheduler: Scheduler = system.scheduler + implicit val executor: ExecutionContext = system.dispatcher + implicit val materializer: ActorMaterializer = ActorMaterializer() + implicit val requestTimeout: Timeout = Timeout(30 seconds) + + val newrelicCheck: ActorRef = system.actorOf(StatusCheckActor.props, "StatusCheck") + + def customTx(s: String): Directive0 = mapResponse { resp => + NewRelic.addCustomParameter("attr1", "newrelic-test") + NewRelic.addCustomParameter("attr2", s) + resp + } + + def customDirective: Directive1[Option[String]] = { + optionalHeaderValueByName("X-NewRelic-Directive").flatMap { + case Some(directiveHeader) => + onComplete(AsyncApp.asyncTest(directiveHeader)).flatMap { + case Success(result) => BasicDirectives.provide(result) + case _ => + BasicDirectives.provide(None) + } + case None => BasicDirectives.provide(None) + } + } + + def akkaStreamDirective: Directive1[String] = { + extractDataBytes.flatMap { data => + val body = data.runFold(ByteString.empty)(_ ++ _).map(_.utf8String) + + onSuccess(body).flatMap { payload => + provide(payload) + } + } + } + + def hostnameAndPort: Directive[(String, Int)] = Directive[(String, Int)] { inner => + ctx => + val authority = ctx.request.uri.authority + inner((authority.host.address(), authority.port))(ctx) + } + + val routes: Route = rejectEmptyResponse { + customDirective { _: Option[String] => + pathPrefix("custom-directive") { + pathPrefix("v2") { + pathPrefix("docs") { + parameters('parameter) { parameterId: String => + get { ctx: RequestContext => + ctx.complete("CustomDirectiveDocsParam: " + parameterId) + } + } ~ + path(Segment) { segment => + get { ctx: RequestContext => + ctx.complete("CustomDirectiveDocsSegment: " + segment) + } + } ~ + path(Segment / "details") { segment => + get { ctx: RequestContext => + ctx.complete("CustomDirectiveDocsSegmentDetails: " + segment) + } + } ~ + path(Segment / "details" / "test") { segment => + get { ctx: RequestContext => + ctx.complete("CustomDirectiveDocsSegmentDetailsTest: " + segment) + } + } + } + } + } ~ + path("callid" / Segment) { id => + val fut: Future[String] = (newrelicCheck ? Ping(id.toInt)).map { + case str: String => s"${str}_id" + case _ => "ERROR" + } + + onSuccess(fut) { response => + customTx("test") { + respondWithHeaders(RawHeader("Test-Header", "async-directive")) { + complete(response) + } + } + } + } ~ + akkaStreamDirective { implicit data => { + path("prefix-first") { + get { ctx: RequestContext => + ctx.complete("prefix-first") + } + } ~ + path("prefix-first-future") { + onSuccess(Future { + "prefix-first-future" + }) { + result => complete(result) + } + } ~ + path("prefix-first-second") { + get { ctx: RequestContext => + ctx.complete("prefix-first-second") + } + } ~ + path("prefix-first-second-future") { + onSuccess(Future { + "prefix-first-second-future" + }) { + result => complete(result) + } + } ~ + path("simple" / "route") { + get { ctx: RequestContext => + ctx.complete("Simple Route") + } + } ~ + path("simple" / "route" / "future") { + onSuccess(Future { + "Simple Route Future" + }) { + result => complete(result) + } + } ~ + path("uuid" / JavaUUID) { uuid => + get { ctx: RequestContext => + ctx.complete("UUID: " + uuid.toString) + } + } ~ + path("uuid" / "future" / JavaUUID) { uuid => + onSuccess(Future { + "UUID Future: " + uuid.toString + }) { + result => complete(result) + } + } ~ + path("regex" / """\d+""".r) { digit => + get { ctx: RequestContext => + ctx.complete("Regex: " + digit) + } + } ~ + path("regex" / "future" / """\d+""".r) { digit => + onSuccess(Future { + "Regex Future: " + digit + }) { + result => complete(result) + } + } ~ + path("map" / Map("red" -> 1, "green" -> 2, "blue" -> 3)) { value => + get { ctx: RequestContext => + ctx.complete("Map: " + value) + } + } ~ + path("map" / "future" / Map("red" -> 1, "green" -> 2, "blue" -> 3)) { value => + onSuccess(Future { + "Map Future: " + value + }) { + result => complete(result) + } + } ~ + path("segment" / "foo" ~ Segment) { segment => + get { ctx: RequestContext => + ctx.complete("Segment: " + segment) + } + } ~ + path("segment" / "future" / "foo" ~ Segment) { segment => + onSuccess(Future { + "Segment Future: " + segment + }) { + result => complete(result) + } + } ~ + pathPrefix("pathend") { + pathEnd { + get { ctx: RequestContext => + ctx.complete("PathEnd") + } + } ~ + path(Segment) { segment => + get { ctx: RequestContext => + ctx.complete("PathEnd: " + segment) + } + } + } ~ + pathPrefix("pathendfuture") { + pathEnd { + onSuccess(Future { + "PathEndFuture" + }) { + result => complete(result) + } + } ~ + path(Segment) { segment => + onSuccess(Future { + "PathEndFuture: " + segment + }) { + result => complete(result) + } + } + } ~ + path("remainingpath" / RemainingPath) { remainingpath => + get { ctx: RequestContext => + ctx.complete("RemainingPath: " + remainingpath) + } + } ~ + path("futureremainingpath" / RemainingPath) { remainingpath => + onSuccess(Future { + "FutureRemainingPath: " + remainingpath + }) { + result => complete(result) + } + } ~ + path("remain" ~ Remaining) { remaining => + get { ctx: RequestContext => + ctx.complete("Remain: " + remaining) + } + } ~ + path("futureremain" ~ Remaining) { remaining => + onSuccess(Future { + "FutureRemain: " + remaining + }) { + result => complete(result) + } + } ~ + path("int" / IntNumber) { number => + get { ctx: RequestContext => + ctx.complete("IntNumber: " + number) + } + } ~ + path("intfuture" / IntNumber) { number => + onSuccess(Future { + "IntNumberFuture: " + number + }) { + result => complete(result) + } + } ~ + path("long" / LongNumber) { number => + get { ctx: RequestContext => + ctx.complete("LongNumber: " + number) + } + } ~ + path("longfuture" / LongNumber) { number => + onSuccess(Future { + "LongNumberFuture: " + number + }) { + result => complete(result) + } + } ~ + path("hexint" / HexIntNumber) { number => + get { ctx: RequestContext => + ctx.complete("HexIntNumber: " + number) + } + } ~ + path("hexintfuture" / HexIntNumber) { number => + onSuccess(Future { + "HexIntNumberFuture: " + number + }) { + result => complete(result) + } + } ~ + path("hexlong" / HexLongNumber) { number => + get { ctx: RequestContext => + ctx.complete("HexLongNumber: " + number) + } + } ~ + path("hexlongfuture" / HexLongNumber) { number => + onSuccess(Future { + "HexLongNumberFuture: " + number + }) { + result => complete(result) + } + } ~ + path("double" / DoubleNumber) { number => + get { ctx: RequestContext => + ctx.complete("DoubleNumber: " + number) + } + } ~ + path("double" / "future" / DoubleNumber) { number => + onSuccess(Future { + "DoubleNumberFuture: " + number + }) { + result => complete(result) + } + } ~ + path("segments" / Segments) { segments => + get { ctx: RequestContext => + val result = segments.mkString(",") + ctx.complete("Segments: " + result) + } + } ~ + path("futuresegments" / Segments) { segments => + onSuccess(Future { + val result = segments.mkString(",") + "FutureSegments: " + result + }) { + result => complete(result) + } + } ~ + path("repeat" / IntNumber.repeat(2, 3, separator = Slash) / "complex") { segments => + get { ctx: RequestContext => + val result = segments.mkString(",") + ctx.complete("Repeat: " + result) + } + } ~ + path("futurerepeat" / IntNumber.repeat(2, 3, separator = Slash) / "complex") { segments => + onSuccess(Future { + val result = segments.mkString(",") + "FutureRepeat: " + result + }) { + result => complete(result) + } + } ~ + path("zerorepeat" / IntNumber.repeat(0, 2, separator = Slash)) { segments => + get { ctx: RequestContext => + val result = segments.mkString(",") + ctx.complete("ZeroRepeat: " + result) + } + } ~ + path("pipe" / ("i" ~ IntNumber | "h" ~ HexIntNumber)) { number => + get { ctx: RequestContext => + ctx.complete("Pipe: " + number) + } + } ~ + path("futurepipe" / ("i" ~ IntNumber | "h" ~ HexIntNumber)) { number => + onSuccess(Future { + "FuturePipe: " + number + }) { + result => complete(result) + } + } ~ + path("pipe" / "optional" / "X" ~ IntNumber.? / ("edit" | "create")) { number => + get { ctx: RequestContext => + ctx.complete("Pipe + Optional: " + number.orNull) + } + } ~ + path("futurepipe" / "optional" / "X" ~ IntNumber.? / ("edit" | "create")) { number => + onSuccess(Future { + "FuturePipe + Optional: " + number.orNull + }) { + result => complete(result) + } + } ~ + pathPrefix("match" ~ !"nomatch") { + get { ctx: RequestContext => + ctx.complete("Negation") + } + } ~ + pathPrefix("futurematch" ~ !"nomatch") { + onSuccess(Future { + "FutureNegation" + }) { + result => complete(result) + } + } ~ + pathPrefix("v1") { + pathPrefix("containers") { + parameters('parameter) { parameterId: String => + get { ctx: RequestContext => + ctx.complete("ContainersParam: " + parameterId) + } + } ~ + path(Segment) { segment => + get { ctx: RequestContext => + ctx.complete("ContainersSegment: " + segment) + } + } ~ + path(Segment / "details") { segment => + get { ctx: RequestContext => + ctx.complete("ContainersSegmentDetails: " + segment) + } + } ~ + path(Segment / "details" / "test") { segment => + get { ctx: RequestContext => + ctx.complete("ContainersSegmentDetailsTest: " + segment) + } + } + } + } ~ + path("future" / IntNumber) { millis => + val futureResult = after(millis.millis, scheduler)(Future.successful(StatusCodes.OK)) + complete(ToResponseMarshallable.apply(futureResult)) + } ~ + path("hostandport") { + hostnameAndPort { + (_, _) => complete(StatusCodes.OK) + } + } ~ + path("test-error") { + throw new UnsupportedOperationException + } ~ + path("test-error-2") { + complete(StatusCodes.InternalServerError, "ErrorTest") + } ~ + pathPrefix("path-end") { + path(Remaining) { + path => { + complete(StatusCodes.OK, "Remaining: " + path) + } + } ~ + pathEnd { + get { + complete(StatusCodes.OK, "Get path end!") + } + } + } ~ + pathPrefix("path-prefix-end") { + path("first-case") { + complete(StatusCodes.OK, "First case") + } ~ + path(Remaining) { + path => { + complete(StatusCodes.OK, "Remaining: " + path) + } + } ~ + pathEnd { + get { + complete(StatusCodes.OK, "Get path end!") + } + } + } + } + } + } + } +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala b/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala new file mode 100644 index 0000000000..cb2f82f8af --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala @@ -0,0 +1,22 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http + +import java.util.concurrent.Executors +import scala.concurrent.{ExecutionContext, ExecutionContextExecutor, Future} + +object AsyncApp { + + implicit val executor: ExecutionContextExecutor = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(2)) + + def asyncTest(header: String): Future[Option[String]] = { + Future { + Some(header) + } + } +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala b/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala new file mode 100644 index 0000000000..68fa0250cc --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala @@ -0,0 +1,56 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http + +import akka.actor.ActorSystem +import akka.event.Logging +import akka.http.scaladsl.Http +import akka.http.scaladsl.Http.ServerBinding +import akka.http.scaladsl.server.Directives._ +import akka.http.scaladsl.server.{RequestContext, Route} +import akka.stream.ActorMaterializer +import akka.util.Timeout +import com.typesafe.config.ConfigFactory + +import scala.concurrent.duration._ +import scala.concurrent.{Await, Future} + +class HttpServer(val routes: Route = RouteService.defaultRoute) { + implicit val system = ActorSystem() + implicit val executor = system.dispatcher + implicit val materializer = ActorMaterializer() + implicit val timeout: Timeout = 60 seconds + + val config = ConfigFactory.load() + val logger = Logging(system, getClass) + + var handle: Future[ServerBinding] = _ + + def start(port: Int) = { + Await.ready({ + handle = Http().bindAndHandle(routes, "localhost", port) + handle + }, timeout.duration) + } + + def stop() = { + if (handle != null) { + handle.flatMap(_.unbind()).onComplete(_ => system.terminate()) + } + } +} + +object RouteService { + val defaultRoute = { + path("test") { + get { (ctx: RequestContext) => + ctx.complete("FAIL") + } + } + } +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala b/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala new file mode 100644 index 0000000000..3cb5c9dac8 --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala @@ -0,0 +1,26 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http + +import akka.http.scaladsl.server.Route +import org.junit.rules.ExternalResource + +import scala.beans.BeanProperty + +class HttpServerRule(@BeanProperty val port: Int, val route: Route) + extends ExternalResource { + val server = new HttpServer(route) + + override def before(): Unit = server.start(port) + + override def after(): Unit = server.stop() +} + +object HttpServerRule { + def apply(port: Int, route: Route) = new HttpServerRule(port, route) +} diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala b/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala new file mode 100644 index 0000000000..46b1518e0c --- /dev/null +++ b/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala @@ -0,0 +1,43 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.agent.instrumentation.akka.http + +import akka.actor.{Actor, ActorRef, Props, Timers} + +import java.util.concurrent.TimeUnit +import scala.concurrent.duration.FiniteDuration +import scala.util.Random + +object StatusCheckActor { + def props: Props = Props(new StatusCheckActor) + + case class Ping(id: Int) + + case object Pong + +} + +class StatusCheckActor extends Actor with Timers { + + import StatusCheckActor._ + + val random: Random = Random + var lastSender: Option[ActorRef] = None + + timers.startPeriodicTimer("my-timer", Pong, FiniteDuration(1, TimeUnit.SECONDS)) + + override def receive: Receive = { + case Ping(id) if id < 100 => + sender ! "Ping_OK" + case Ping(id) => + lastSender = Some(sender) + case Pong => + lastSender.foreach(_ ! "Pong_OK") + lastSender = None + } +} diff --git a/settings.gradle b/settings.gradle index d00fde7a88..300b384950 100644 --- a/settings.gradle +++ b/settings.gradle @@ -55,6 +55,7 @@ include 'instrumentation:aws-java-sdk-sns-2.0' include 'instrumentation:aws-wrap-0.7.0' include 'instrumentation:akka-2.2' include 'instrumentation:akka-http-2.4.5' +include 'instrumentation:akka-http-2.13_2.4.5' include 'instrumentation:akka-http-core-0.4' include 'instrumentation:akka-http-core-0.7' include 'instrumentation:akka-http-core-1.0' From e84acf426942b2278bd3f71a1bb8be62670a8f88 Mon Sep 17 00:00:00 2001 From: jasonjkeller Date: Thu, 8 Apr 2021 11:06:32 -0700 Subject: [PATCH 5/9] Rename akka-http-2.4.5 modules and fix verifier ranges --- .../build.gradle | 13 ++++++++----- .../http/scaladsl/marshalling/AkkaHttpMarshal.java | 0 .../marshalling/AkkaHttpMarshallerMapper.java | 0 .../marshalling/AkkaHttpToResponseMarshallable.java | 0 .../scaladsl/server/AkkaHttpContextFunction.scala | 0 .../http/scaladsl/server/AkkaHttpPathMatchers.java | 0 .../scaladsl/server/AkkaHttpRequestContext.scala | 0 .../scaladsl/server/Directive_Instrumentation.java | 0 ...citPathMatcherConstruction_Instrumentation.scala | 0 .../server/NewRelicRequestContextWrapper.scala | 0 .../server/PathMatcher_Instrumentation.scala | 0 .../server/directives/AkkaExecutionDirectives.java | 0 .../server/directives/OnSuccessMagnetDirective.java | 0 .../instrumentation/akka/http/Function0Wrapper.java | 0 .../instrumentation/akka/http/Function1Wrapper.java | 0 .../instrumentation/akka/http/FutureWrapper.java | 0 .../instrumentation/akka/http/InboundWrapper.scala | 0 .../instrumentation/akka/http/OutboundWrapper.scala | 0 .../akka/http/PathMatcherScalaUtils.scala | 0 .../instrumentation/akka/http/PathMatcherUtils.java | 0 .../instrumentation/akka/http/RequestWrapper.scala | 0 .../instrumentation/akka/http/ResponseWrapper.scala | 0 .../akka/http/AkkaHttpRoutesTest.java | 0 .../akka/http/AkkaResponseWrapperTest.java | 0 .../akka/http/AkkaHttpTestRoutes.scala | 0 .../agent/instrumentation/akka/http/AsyncApp.scala | 0 .../instrumentation/akka/http/HttpServer.scala | 0 .../instrumentation/akka/http/HttpServerRule.scala | 0 .../akka/http/StatusCheckActor.scala | 0 .../build.gradle | 2 +- .../http/scaladsl/marshalling/AkkaHttpMarshal.java | 0 .../marshalling/AkkaHttpMarshallerMapper.java | 0 .../marshalling/AkkaHttpToResponseMarshallable.java | 0 .../scaladsl/server/AkkaHttpContextFunction.scala | 0 .../http/scaladsl/server/AkkaHttpPathMatchers.java | 0 .../scaladsl/server/AkkaHttpRequestContext.scala | 0 .../scaladsl/server/Directive_Instrumentation.java | 0 ...citPathMatcherConstruction_Instrumentation.scala | 0 .../server/NewRelicRequestContextWrapper.scala | 0 .../server/PathMatcher_Instrumentation.scala | 0 .../server/directives/AkkaExecutionDirectives.java | 0 .../server/directives/OnSuccessMagnetDirective.java | 0 .../instrumentation/akka/http/Function0Wrapper.java | 0 .../instrumentation/akka/http/Function1Wrapper.java | 0 .../instrumentation/akka/http/FutureWrapper.java | 0 .../instrumentation/akka/http/InboundWrapper.scala | 0 .../instrumentation/akka/http/OutboundWrapper.scala | 0 .../akka/http/PathMatcherScalaUtils.scala | 0 .../instrumentation/akka/http/PathMatcherUtils.java | 0 .../instrumentation/akka/http/RequestWrapper.scala | 0 .../instrumentation/akka/http/ResponseWrapper.scala | 0 .../akka/http/AkkaHttpRoutesTest.java | 0 .../akka/http/AkkaResponseWrapperTest.java | 0 .../akka/http/AkkaHttpTestRoutes.scala | 0 .../agent/instrumentation/akka/http/AsyncApp.scala | 0 .../instrumentation/akka/http/HttpServer.scala | 0 .../instrumentation/akka/http/HttpServerRule.scala | 0 .../akka/http/StatusCheckActor.scala | 0 settings.gradle | 4 ++-- 59 files changed, 11 insertions(+), 8 deletions(-) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/build.gradle (74%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.11_2.4.5}/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.11_2.4.5}/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.11_2.4.5}/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/build.gradle (96%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.13_10.1.8}/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala (100%) rename instrumentation/{akka-http-2.4.5 => akka-http-2.13_10.1.8}/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala (100%) rename instrumentation/{akka-http-2.13_2.4.5 => akka-http-2.13_10.1.8}/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala (100%) diff --git a/instrumentation/akka-http-2.4.5/build.gradle b/instrumentation/akka-http-2.11_2.4.5/build.gradle similarity index 74% rename from instrumentation/akka-http-2.4.5/build.gradle rename to instrumentation/akka-http-2.11_2.4.5/build.gradle index 37925259a1..fe08471985 100644 --- a/instrumentation/akka-http-2.4.5/build.gradle +++ b/instrumentation/akka-http-2.11_2.4.5/build.gradle @@ -4,7 +4,7 @@ sourceSets.test.scala.srcDir "src/test/java" sourceSets.test.java.srcDirs = [] jar { - manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.akka-http-2.4.5' } + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.akka-http-2.11_2.4.5' } } dependencies { @@ -21,18 +21,21 @@ dependencies { verifyInstrumentation { passesOnly 'com.typesafe.akka:akka-http-experimental_2.11:[2.4.5,)' - passesOnly 'com.typesafe.akka:akka-http-experimental_2.10:[2.4.5,)' - passesOnly 'com.typesafe.akka:akka-http_2.10:[2.4.5,)' - passesOnly('com.typesafe.akka:akka-http_2.11:[2.4.5,)') { + passesOnly('com.typesafe.akka:akka-http_2.11:[10.0.0,)') { compile("com.typesafe.akka:akka-stream_2.11:2.5.19") } - passesOnly('com.typesafe.akka:akka-http_2.12:[2.4.5,)') { + passesOnly('com.typesafe.akka:akka-http_2.12:[10.0.0,)') { compile("com.typesafe.akka:akka-stream_2.11:2.5.19") } + fails('com.typesafe.akka:akka-http_2.13:[10.1.8,)') excludeRegex 'com.typesafe.akka:akka-http-experimental_2.11:.*(RC|M)[0-9]*$' + excludeRegex 'com.typesafe.akka:akka-http_2.11:.*(RC|M)[0-9]*$' + excludeRegex 'com.typesafe.akka:akka-http_2.12:.*(RC|M)[0-9]*$' + excludeRegex 'com.typesafe.akka:akka-http_2.13:.*(RC|M)[0-9]*$' excludeRegex 'com.typesafe.akka:akka-http_2.11:.*-[0-9a-f]{8}$' excludeRegex 'com.typesafe.akka:akka-http_2.12:.*-[0-9a-f]{8}$' + excludeRegex 'com.typesafe.akka:akka-http_2.13:.*-[0-9a-f]{8}$' } site { diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala b/instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala rename to instrumentation/akka-http-2.11_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java b/instrumentation/akka-http-2.11_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java rename to instrumentation/akka-http-2.11_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java diff --git a/instrumentation/akka-http-2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java b/instrumentation/akka-http-2.11_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java similarity index 100% rename from instrumentation/akka-http-2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java rename to instrumentation/akka-http-2.11_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java diff --git a/instrumentation/akka-http-2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala b/instrumentation/akka-http-2.11_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala rename to instrumentation/akka-http-2.11_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala diff --git a/instrumentation/akka-http-2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala b/instrumentation/akka-http-2.11_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala rename to instrumentation/akka-http-2.11_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala diff --git a/instrumentation/akka-http-2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala b/instrumentation/akka-http-2.11_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala rename to instrumentation/akka-http-2.11_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala diff --git a/instrumentation/akka-http-2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala b/instrumentation/akka-http-2.11_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala rename to instrumentation/akka-http-2.11_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala diff --git a/instrumentation/akka-http-2.4.5/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala b/instrumentation/akka-http-2.11_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala rename to instrumentation/akka-http-2.11_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/build.gradle b/instrumentation/akka-http-2.13_10.1.8/build.gradle similarity index 96% rename from instrumentation/akka-http-2.13_2.4.5/build.gradle rename to instrumentation/akka-http-2.13_10.1.8/build.gradle index 05f16e2233..ee9a06233b 100644 --- a/instrumentation/akka-http-2.13_2.4.5/build.gradle +++ b/instrumentation/akka-http-2.13_10.1.8/build.gradle @@ -4,7 +4,7 @@ sourceSets.test.scala.srcDir "src/test/java" sourceSets.test.java.srcDirs = [] jar { - manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.akka-http-2.13_2.4.5' } + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.akka-http-2.13_10.1.8' } } dependencies { diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshal.java diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpMarshallerMapper.java diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/marshalling/AkkaHttpToResponseMarshallable.java diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/AkkaHttpContextFunction.scala diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/AkkaHttpPathMatchers.java diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/AkkaHttpRequestContext.scala diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/Directive_Instrumentation.java diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/ImplicitPathMatcherConstruction_Instrumentation.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/NewRelicRequestContextWrapper.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/PathMatcher_Instrumentation.scala diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/directives/AkkaExecutionDirectives.java diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/akka/http/scaladsl/server/directives/OnSuccessMagnetDirective.java diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/Function0Wrapper.java diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/Function1Wrapper.java diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/FutureWrapper.java diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/InboundWrapper.scala diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/OutboundWrapper.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherScalaUtils.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/PathMatcherUtils.java diff --git a/instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/RequestWrapper.scala diff --git a/instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala b/instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala similarity index 100% rename from instrumentation/akka-http-2.4.5/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala rename to instrumentation/akka-http-2.13_10.1.8/src/main/scala/com/agent/instrumentation/akka/http/ResponseWrapper.scala diff --git a/instrumentation/akka-http-2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java b/instrumentation/akka-http-2.13_10.1.8/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java similarity index 100% rename from instrumentation/akka-http-2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java rename to instrumentation/akka-http-2.13_10.1.8/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java b/instrumentation/akka-http-2.13_10.1.8/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java rename to instrumentation/akka-http-2.13_10.1.8/src/test/java/com/agent/instrumentation/akka/http/AkkaResponseWrapperTest.java diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala b/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala rename to instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala b/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala rename to instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/AsyncApp.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala b/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala rename to instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala b/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala rename to instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/HttpServerRule.scala diff --git a/instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala b/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala similarity index 100% rename from instrumentation/akka-http-2.13_2.4.5/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala rename to instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/StatusCheckActor.scala diff --git a/settings.gradle b/settings.gradle index 300b384950..ddfedc6c5f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -54,8 +54,8 @@ include 'instrumentation:aws-java-sdk-dynamodb-1.11.106' include 'instrumentation:aws-java-sdk-sns-2.0' include 'instrumentation:aws-wrap-0.7.0' include 'instrumentation:akka-2.2' -include 'instrumentation:akka-http-2.4.5' -include 'instrumentation:akka-http-2.13_2.4.5' +include 'instrumentation:akka-http-2.11_2.4.5' +include 'instrumentation:akka-http-2.13_10.1.8' include 'instrumentation:akka-http-core-0.4' include 'instrumentation:akka-http-core-0.7' include 'instrumentation:akka-http-core-1.0' From fdf4d31ff2763befde0f4975cb17b08a742c216a Mon Sep 17 00:00:00 2001 From: jasonjkeller Date: Fri, 9 Apr 2021 13:56:26 -0700 Subject: [PATCH 6/9] Fix test by explicitly enabling postfix operator --- .../com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala b/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala index 1f7b39f4e6..01b8942784 100644 --- a/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala +++ b/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala @@ -23,6 +23,7 @@ import com.newrelic.api.agent.NewRelic import scala.concurrent.duration._ import scala.concurrent.{ExecutionContext, Future} import scala.util.Success +import scala.language.postfixOps class AkkaHttpTestRoutes { From 89e7c8c6bffb1175f3935cf37bc7e1bb6625210d Mon Sep 17 00:00:00 2001 From: jasonjkeller Date: Fri, 9 Apr 2021 14:21:05 -0700 Subject: [PATCH 7/9] Fix another test with postfix op --- .../scala/com/agent/instrumentation/akka/http/HttpServer.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala b/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala index 68fa0250cc..861ae4f17e 100644 --- a/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala +++ b/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/HttpServer.scala @@ -19,6 +19,7 @@ import com.typesafe.config.ConfigFactory import scala.concurrent.duration._ import scala.concurrent.{Await, Future} +import scala.language.postfixOps class HttpServer(val routes: Route = RouteService.defaultRoute) { implicit val system = ActorSystem() From 1b585242f8c5b6da61c7ea172f603d5aa048292b Mon Sep 17 00:00:00 2001 From: jasonjkeller Date: Fri, 9 Apr 2021 16:31:50 -0700 Subject: [PATCH 8/9] Cleanup --- instrumentation/akka-http-2.13_10.1.8/build.gradle | 2 +- .../instrumentation/akka/http/AkkaHttpTestRoutes.scala | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/instrumentation/akka-http-2.13_10.1.8/build.gradle b/instrumentation/akka-http-2.13_10.1.8/build.gradle index ee9a06233b..e0069c2c6d 100644 --- a/instrumentation/akka-http-2.13_10.1.8/build.gradle +++ b/instrumentation/akka-http-2.13_10.1.8/build.gradle @@ -20,7 +20,7 @@ dependencies { } verifyInstrumentation { - passesOnly('com.typesafe.akka:akka-http_2.13:[2.4.5,)') { + passesOnly('com.typesafe.akka:akka-http_2.13:[10.1.8,)') { compile("com.typesafe.akka:akka-stream_2.13:2.5.23") } excludeRegex 'com.typesafe.akka:akka-http_2.13:.*(RC|M)[0-9]*$' diff --git a/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala b/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala index 01b8942784..023aea7796 100644 --- a/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala +++ b/instrumentation/akka-http-2.13_10.1.8/src/test/scala/com/agent/instrumentation/akka/http/AkkaHttpTestRoutes.scala @@ -1,7 +1,7 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 * */ @@ -74,7 +74,7 @@ class AkkaHttpTestRoutes { pathPrefix("custom-directive") { pathPrefix("v2") { pathPrefix("docs") { - parameters('parameter) { parameterId: String => + parameters(Symbol("parameter")) { parameterId: String => get { ctx: RequestContext => ctx.complete("CustomDirectiveDocsParam: " + parameterId) } @@ -380,7 +380,7 @@ class AkkaHttpTestRoutes { } ~ pathPrefix("v1") { pathPrefix("containers") { - parameters('parameter) { parameterId: String => + parameters(Symbol("parameter")) { parameterId: String => get { ctx: RequestContext => ctx.complete("ContainersParam: " + parameterId) } From 8c3ae8b25249249f204398c236aef65ebbf72ba5 Mon Sep 17 00:00:00 2001 From: jasonjkeller Date: Thu, 22 Apr 2021 15:53:14 -0700 Subject: [PATCH 9/9] Fix instrumentation test --- .../com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/akka-http-2.13_10.1.8/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java b/instrumentation/akka-http-2.13_10.1.8/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java index 9f5683b1d2..15f5eeed77 100644 --- a/instrumentation/akka-http-2.13_10.1.8/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java +++ b/instrumentation/akka-http-2.13_10.1.8/src/test/java/com/agent/instrumentation/akka/http/AkkaHttpRoutesTest.java @@ -28,7 +28,7 @@ import static org.hamcrest.Matchers.containsString; @RunWith(InstrumentationTestRunner.class) -@InstrumentationTestConfig(includePrefixes = { "akka", "scala", "com.agent", "com.nr" }) +@InstrumentationTestConfig(includePrefixes = { "akka", "scala" }) public class AkkaHttpRoutesTest { private static final long TIMEOUT = 30000;