diff --git a/dependencies.gradle b/dependencies.gradle
index 8a3b28a7a..399278995 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -31,8 +31,10 @@ configurations {
sharedCsProd {
extendsFrom configurations.sharedAllProd
}
+ agentJavaSdkProd
agentJavaProd {
extendsFrom configurations.sharedAllProd
+ extendsFrom configurations.agentJavaSdkProd
}
serverProd {
extendsFrom configurations.sharedAllProd
@@ -56,6 +58,9 @@ configurations {
agentJavaTest {
extendsFrom configurations.testbase
}
+ agentJavaSdkTest {
+ extendsFrom configurations.testbase
+ }
serverTest {
extendsFrom configurations.testbase
extendsFrom configurations.classmexer
@@ -85,6 +90,7 @@ configurations {
configurations.sharedAllProd.transitive = false
configurations.sharedCsProd.transitive = false
configurations.agentJavaProd.transitive = false
+configurations.agentJavaSdkProd.transitive = false
configurations.serverProd.transitive = false
configurations.serverDiagnosisProd.transitive = false
configurations.uiRcpProd.transitive = false
@@ -93,6 +99,7 @@ configurations.classmexer.transitive = false
configurations.sharedAllTest.transitive = false
configurations.sharedCsTest.transitive = false
configurations.agentJavaTest.transitive = false
+configurations.agentJavaSdkTest.transitive = false
configurations.serverTest.transitive = false
configurations.serverDiagnosisTest.transitive = false
configurations.uiRcpTest.transitive = false
@@ -147,6 +154,12 @@ dependencies {
sharedCsTest (
'nl.jqno.equalsverifier:equalsverifier:1.0.2'
)
+
+ /** inspectit.agent.java.sdk */
+ agentJavaSdkProd (
+ 'io.opentracing:opentracing-api:0.20.9',
+ 'io.opentracing:opentracing-noop:0.20.9',
+ )
/** inspectit.agent.java */
agentJavaProd (
diff --git a/inspectit.agent.java.sdk/.checkstyle b/inspectit.agent.java.sdk/.checkstyle
new file mode 100644
index 000000000..95cba8f52
--- /dev/null
+++ b/inspectit.agent.java.sdk/.checkstyle
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/inspectit.agent.java.sdk/.classpath b/inspectit.agent.java.sdk/.classpath
new file mode 100644
index 000000000..d6b05322f
--- /dev/null
+++ b/inspectit.agent.java.sdk/.classpath
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/inspectit.agent.java.sdk/.eclipse-pmd b/inspectit.agent.java.sdk/.eclipse-pmd
new file mode 100644
index 000000000..2235a96c6
--- /dev/null
+++ b/inspectit.agent.java.sdk/.eclipse-pmd
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/inspectit.agent.java.sdk/.gitignore b/inspectit.agent.java.sdk/.gitignore
new file mode 100644
index 000000000..886828554
--- /dev/null
+++ b/inspectit.agent.java.sdk/.gitignore
@@ -0,0 +1,3 @@
+/bin
+/build
+/test-output
diff --git a/inspectit.agent.java.sdk/.project b/inspectit.agent.java.sdk/.project
new file mode 100644
index 000000000..e8daf771c
--- /dev/null
+++ b/inspectit.agent.java.sdk/.project
@@ -0,0 +1,35 @@
+
+
+ inspectit.agent.java.sdk
+ Project inspectit.agent.java.sdk created by Buildship.
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+ net.sf.eclipsecs.core.CheckstyleBuilder
+
+
+
+
+ ch.acanda.eclipse.pmd.builder.PMDBuilder
+
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectnature
+ org.eclipse.jdt.core.javanature
+ net.sf.eclipsecs.core.CheckstyleNature
+ ch.acanda.eclipse.pmd.builder.PMDNature
+
+
diff --git a/inspectit.agent.java.sdk/.settings/org.eclipse.buildship.core.prefs b/inspectit.agent.java.sdk/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 000000000..e6133b74f
--- /dev/null
+++ b/inspectit.agent.java.sdk/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,11 @@
+build.commands=org.eclipse.jdt.core.javabuilder
+connection.arguments=
+connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
+connection.java.home=null
+connection.jvm.arguments=
+connection.project.dir=..
+containers=org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6/
+derived.resources=.gradle,build
+eclipse.preferences.version=1
+natures=org.eclipse.jdt.core.javanature
+project.path=\:inspectit.agent.java.sdk
diff --git a/inspectit.agent.java.sdk/.settings/org.eclipse.jdt.core.prefs b/inspectit.agent.java.sdk/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..1c651f90c
--- /dev/null
+++ b/inspectit.agent.java.sdk/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,13 @@
+eclipse.preferences.version=1
+instance/org.eclipse.core.net/org.eclipse.core.net.hasMigrated=true
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
diff --git a/inspectit.agent.java.sdk/inspectit.agent.java.sdk.gradle b/inspectit.agent.java.sdk/inspectit.agent.java.sdk.gradle
new file mode 100644
index 000000000..df04168ef
--- /dev/null
+++ b/inspectit.agent.java.sdk/inspectit.agent.java.sdk.gradle
@@ -0,0 +1,63 @@
+/**
+ * Gradle build file for the inspectit.agent.java.sdk project.
+ *
+ * @author Ivan Senic
+ */
+defaultTasks 'releaseAndAnalyze'
+
+/** used by the eclipse buildship plugin */
+sourceCompatibility = '1.6'
+targetCompatibility = '1.6'
+
+/** defined to have it included in Eclipse as source */
+sourceSets {
+ externalResources {
+ resources {
+ srcDir mainExtResources
+ }
+ }
+}
+
+/** Some agent specific properties */
+ext {
+ distJarName = 'inspectit-agent-java-sdk'
+}
+
+/** Setting compile configuration as plugin in Eclipse needs it */
+configurations {
+ compile {
+ extendsFrom configurations.agentJavaSdkProd
+ }
+ testCompile {
+ extendsFrom configurations.agentJavaSdkTest
+ extendsFrom configurations.jmhbase
+ }
+}
+
+/** Must depend on shared all test sources because of TestBase class */
+dependencies {
+ testCompile project (path: ':inspectit.shared.all', configuration: 'testArchives')
+}
+
+/** Compile compatibility to 1.6 for all compile tasks */
+tasks.withType(JavaCompile) { t ->
+ sourceCompatibility = '1.6'
+ targetCompatibility = '1.6'
+
+ options.bootClasspath = configurations.java16rt.asPath
+}
+
+/**
+ * Creates the jar.
+ */
+jar {
+ archivesBaseName = distJarName
+
+ manifest {}
+}
+
+task releaseAndAnalyze {
+ description = "Runs all unit tests, all checks and releases the jar."
+ group = 'Release'
+ dependsOn(analyze, jar)
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/ExtendedTracer.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/ExtendedTracer.java
new file mode 100644
index 000000000..209f31183
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/ExtendedTracer.java
@@ -0,0 +1,81 @@
+package rocks.inspectit.agent.java.sdk.opentracing;
+
+import io.opentracing.Tracer;
+import io.opentracing.propagation.Format;
+import rocks.inspectit.agent.java.sdk.opentracing.propagation.Propagator;
+
+/**
+ * This interface defines additional methods that inspectIT tracer provides for usage.
+ *
+ * There are additional two methods for creating a {@link SpanBuilder} where user can choose if
+ * current thread context should be referenced or not.
+ *
+ * The tracer allows registration of the {@link Propagator} for a specific format. This method
+ * allows overwriting of the tracer default propagators. Note that inspectIT tracer by default uses
+ * {@link rocks.inspectit.agent.java.sdk.opentracing.internal.propagation.TextMapPropagator} for the
+ * io.opentracing.propagation.Format.Builtin.TEXT_MAP
and
+ * {@link rocks.inspectit.agent.java.sdk.opentracing.internal.propagation.UrlEncodingPropagator} for
+ * the io.opentracing.propagation.Format.Builtin.HTTP_HEADERS
format.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface ExtendedTracer extends Tracer {
+
+ /**
+ * Registers propagator. This method allows overwriting of the tracer default propagators. Note
+ * that inspectIT tracer by default uses
+ * {@link rocks.inspectit.agent.java.sdk.opentracing.internal.propagation.TextMapPropagator} for
+ * the io.opentracing.propagation.Format.Builtin.TEXT_MAP
and
+ * {@link rocks.inspectit.agent.java.sdk.opentracing.internal.propagation.UrlEncodingPropagator}
+ * for the io.opentracing.propagation.Format.Builtin.HTTP_HEADERS
format.
+ *
+ * @param
+ * format type
+ * @param format
+ * opentracing {@link Format}
+ * @param propagator
+ * {@link Propagator}
+ */
+ void registerPropagator(Format format, Propagator propagator);
+
+ /**
+ * Sets the implementation of the {@link Timer} to use.
+ *
+ * By default inspectIT tracer uses
+ * {@link rocks.inspectit.agent.java.sdk.opentracing.util.SystemTimer} that has millisecond
+ * start time precision. This done so inspectIT can be compatible with Java 6. Users can provide
+ * better timers if they run on higher Java versions or have third party dependencies that could
+ * do better.
+ *
+ * @param timer
+ * {@link Timer} to set. Must not be null
.
+ * @throws IllegalArgumentException
+ * If timer provided is null
.
+ */
+ void setTimer(Timer timer) throws IllegalArgumentException;
+
+ /**
+ * Builds span with no operation name. The thread context reference will added if the one exists
+ * as the CHILD_OF reference.
+ *
+ * @return {@link SpanBuilder}.
+ */
+ SpanBuilder buildSpan();
+
+ /**
+ * Creates {@link SpanBuilder} that optionally adds the reference to the current thread context
+ * span.
+ *
+ * @param operationName
+ * Operation name of the span.
+ * @param referenceType
+ * Reference type to the current context. Can be null
if
+ * useThreadContext=false
+ * @param useThreadContext
+ * If thread context should be used.
+ * @return {@link SpanBuilder}.
+ */
+ SpanBuilder buildSpan(String operationName, String referenceType, boolean useThreadContext);
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/Reporter.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/Reporter.java
new file mode 100644
index 000000000..068df3ec7
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/Reporter.java
@@ -0,0 +1,26 @@
+package rocks.inspectit.agent.java.sdk.opentracing;
+
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanImpl;
+
+/**
+ * Reporter serves as class handling finished spans.
+ *
+ * This SDK only provides the {@link rocks.inspectit.agent.java.sdk.opentracing.noop.NoopReporter}
+ * as the implementation. However, if the inspectit agent is active on the target application, the
+ * "real" reporter will be used which sends spans to the inspectIT CMR. In this case the
+ * initialization of the reported and the tracer is done by inspectIT and can be obtained in
+ * {@link rocks.inspectit.agent.java.sdk.opentracing.TracerProvider}.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface Reporter {
+
+ /**
+ * Reports span once it's finished. Spans that are started, but not finished are not reported.
+ *
+ * @param span
+ * Span to report.
+ */
+ void report(SpanImpl span);
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/Timer.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/Timer.java
new file mode 100644
index 000000000..1b30369ea
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/Timer.java
@@ -0,0 +1,25 @@
+package rocks.inspectit.agent.java.sdk.opentracing;
+
+/**
+ * Timer interface for measuring.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface Timer {
+
+ /**
+ * Returns current time in microseconds (microseconds since epoch).
+ *
+ * @return Returns current time in microseconds (microseconds since epoch).
+ */
+ long getCurrentTimeMicroseconds();
+
+ /**
+ * Returns the current nano time. Simple implementation can use
+ * {@link java.lang.System#nanoTime()}.
+ *
+ * @return Returns the current nano time.
+ */
+ long getCurrentNanoTime();
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/TracerProvider.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/TracerProvider.java
new file mode 100644
index 000000000..d37270e65
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/TracerProvider.java
@@ -0,0 +1,77 @@
+package rocks.inspectit.agent.java.sdk.opentracing;
+
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.TracerImpl;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.noop.NoopExtendedTracerImpl;
+
+/**
+ * The tracer provider allows getting of the {@link ExtendedTracer} instance.
+ *
+ * If the inspectIT agent is running with the application where {@link TracerProvider} is used, the
+ * get
methods will always returned correctly initialized {@link ExtendedTracer}.
+ *
+ * If the inspectIT is not running, then the caller can control if the returned tracer is noop
+ * tracer or null
.
+ *
+ * @author Ivan Senic
+ *
+ */
+public final class TracerProvider {
+
+ /**
+ * The initialized implementation of the {@link TracerImpl}.
+ */
+ private static TracerImpl tracer; // NOPMD NOCHK
+
+ /**
+ * No instantiation.
+ */
+ private TracerProvider() {
+ }
+
+ /**
+ * Returns the {@link ExtendedTracer}. This method never returns null
. If the
+ * inspectIT is running, this method will return the correctly initialized inspectIT tracer
+ * implementation that reports data to the inspectIT. If the inspectIT is not running with the
+ * application this method will return the noop tracer.
+ *
+ * Same as calling {@link #get(boolean)} with true
as argument.
+ *
+ * @return {@link ExtendedTracer}
+ */
+ public static ExtendedTracer get() {
+ return get(true);
+ }
+
+ /**
+ * Returns the {@link ExtendedTracer}. If the inspectIT is running, this method will return the
+ * correctly initialized inspectIT tracer implementation that reports data to the inspectIT. If
+ * the inspectIT is not running with the application then value of noopFailback
+ * will be used to determine the return.
+ *
+ * @param noopFallback
+ * Controls what is returned if the inspectIT tracer is not available. Passing
+ * true
will return the noop tracer, while passing false
+ * will return null
.
+ * @return {@link ExtendedTracer}
+ */
+ public static ExtendedTracer get(boolean noopFallback) {
+ if (null != tracer) {
+ return tracer;
+ } else if (noopFallback) {
+ return NoopExtendedTracerImpl.INSTANCE;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Sets {@link #tracer}. Should be called only by inspectIT SDK classes.
+ *
+ * @param tracer
+ * New value for {@link #tracer}
+ */
+ public static void set(TracerImpl tracer) {
+ TracerProvider.tracer = tracer;
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/TracerLogger.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/TracerLogger.java
new file mode 100644
index 000000000..b16f865dd
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/TracerLogger.java
@@ -0,0 +1,99 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal;
+
+/**
+ * Interface that provides the logging ability to the tracer. This interface is defined in order to
+ * not introduce any dependencies.
+ *
+ * This SDK only provides the
+ * {@link rocks.inspectit.agent.java.sdk.opentracing.internal.noop.NoopLogger} as the
+ * implementation. However, if the inspectIT agent is active on the target application, the "real"
+ * SLF4J logger will be used which does the logging. In this case the initialization of the logger
+ * and the tracer is done by inspectIT and can be obtained in
+ * {@link rocks.inspectit.agent.java.sdk.opentracing.TracerProvider}.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface TracerLogger {
+
+ /**
+ * Defines if the info level is enabled.
+ *
+ * @return If the info level is enabled.
+ */
+ boolean isInfoEnabled();
+
+ /**
+ * Log message in info level.
+ *
+ * @param message
+ * Message to log.
+ */
+ void info(String message);
+
+ /**
+ * Defines if the debug level is enabled.
+ *
+ * @return If the debug level is enabled.
+ */
+ boolean isDebugEnabled();
+
+ /**
+ * Log message in debug level.
+ *
+ * @param message
+ * Message to log.
+ */
+ void debug(String message);
+
+ /**
+ * Defines if the warn level is enabled.
+ *
+ * @return If the warn level is enabled.
+ */
+ boolean isWarnEnabled();
+
+ /**
+ * Log message in warn level.
+ *
+ * @param message
+ * Message to log.
+ */
+ void warn(String message);
+
+ /**
+ * Log message in warn level with throwable.
+ *
+ * @param message
+ * Message to log.
+ * @param t
+ * Throwable to log.
+ */
+ void warn(String message, Throwable t);
+
+ /**
+ * Defines if the error level is enabled.
+ *
+ * @return If the error level is enabled.
+ */
+ boolean isErrorEnabled();
+
+ /**
+ * Log message in error level.
+ *
+ * @param message
+ * Message to log.
+ */
+ void error(String message);
+
+ /**
+ * Log message in error level with throwable.
+ *
+ * @param message
+ * Message to log.
+ * @param t
+ * Throwable to log.
+ */
+ void error(String message, Throwable t);
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/TracerLoggerProvider.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/TracerLoggerProvider.java
new file mode 100644
index 000000000..10470f427
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/TracerLoggerProvider.java
@@ -0,0 +1,20 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal;
+
+/**
+ * Provides {@link TracerLogger} for any class that wants to log details related to tracing.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface TracerLoggerProvider {
+
+ /**
+ * Returns {@link TracerLogger} for the given class.
+ *
+ * @param clazz
+ * Class to get logger for.
+ * @return Returns {@link TracerLogger} for the given class.
+ */
+ TracerLogger getTraceLogger(Class> clazz);
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/constants/PropagationConstants.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/constants/PropagationConstants.java
new file mode 100644
index 000000000..e94b76151
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/constants/PropagationConstants.java
@@ -0,0 +1,30 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.constants;
+
+/**
+ * Propagation constants.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface PropagationConstants {
+
+ /**
+ * Prefix for the propagation baggage.
+ */
+ String INSPECTIT_PREFIX = "inspectit_";
+
+ /**
+ * Header name for the span id.
+ */
+ String SPAN_ID = INSPECTIT_PREFIX + "spanid";
+
+ /**
+ * Header name for the trace id.
+ */
+ String TRACE_ID = INSPECTIT_PREFIX + "traceid";
+
+ /**
+ * Prefix for the propagation baggage.
+ */
+ String INSPECTIT_BAGGAGE_PREFIX = INSPECTIT_PREFIX + "baggage_";
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanBuilderImpl.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanBuilderImpl.java
new file mode 100644
index 000000000..f4a4fb99e
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanBuilderImpl.java
@@ -0,0 +1,209 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import io.opentracing.References;
+import io.opentracing.Span;
+import io.opentracing.SpanContext;
+import io.opentracing.Tracer.SpanBuilder;
+import rocks.inspectit.agent.java.sdk.opentracing.Timer;
+
+/**
+ * Builder for the span. Note that builder is delegating the calls to the span that's created
+ * immediately on the builder initialization. This way we save the copying of the data to the span
+ * later on.
+ *
+ * Limitations: This span implementation is saving only one (first) referenced context as the
+ * parent. For any additional referenced contexts only the baggage propagation will be done.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class SpanBuilderImpl implements SpanBuilder {
+
+ /**
+ * Span being created.
+ */
+ private final SpanImpl span;
+
+ /**
+ * Timer to use if startTimestamp is not provided in the builder.
+ */
+ private final Timer timer;
+
+ /**
+ * Collected baggage from all parents.
+ */
+ private final Map baggage = new HashMap();
+
+ /**
+ * Manually specified timestamp.
+ */
+ private long startTimestamp;
+
+ /**
+ * Parent context. Can be null
to denote the new span.
+ */
+ private SpanContextImpl parent;
+
+ /**
+ * Reference type to the {@link #parent} context.
+ */
+ private String referenceType;
+
+ /**
+ * Creates new span builder.
+ *
+ * @param tracer
+ * {@link TracerImpl}
+ * @param operationName
+ * Operation name.
+ */
+ public SpanBuilderImpl(TracerImpl tracer, String operationName) {
+ this.timer = tracer.getTimer();
+ this.span = new SpanImpl(tracer);
+ this.span.setOperationName(operationName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterable> baggageItems() {
+ return baggage.entrySet();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanBuilderImpl asChildOf(SpanContext parent) {
+ if (null != parent) {
+ addReference(References.CHILD_OF, parent);
+ }
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanBuilderImpl asChildOf(Span parent) {
+ if (null != parent) {
+ addReference(References.CHILD_OF, parent.context());
+ }
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * If the {@link #parent} is not set this method will set the passed context as the main
+ * referenced context. All the baggage from the context will be passed no matter if the
+ * {@link #parent} is already set or not.
+ */
+ @Override
+ public SpanBuilderImpl addReference(String referenceType, SpanContext referencedContext) {
+ if (References.CHILD_OF.equals(referenceType) || References.FOLLOWS_FROM.equals(referenceType)) {
+ // we will set the main parent only if it's not set already
+ if ((null == parent) && (referencedContext instanceof SpanContextImpl)) {
+ this.parent = (SpanContextImpl) referencedContext;
+ this.referenceType = referenceType;
+ }
+
+ }
+ // for now we only directly reference one parent, but collect baggage from all
+ // reference contexts
+ withBaggageFrom(referencedContext);
+
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanBuilderImpl withTag(String key, String value) {
+ span.setTag(key, value);
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanBuilderImpl withTag(String key, boolean value) {
+ span.setTag(key, value);
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanBuilderImpl withTag(String key, Number value) {
+ span.setTag(key, value);
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanBuilderImpl withStartTimestamp(long microseconds) {
+ this.startTimestamp = microseconds;
+ return this;
+ }
+
+ /**
+ * Explicitly states that the span created should not be reported.
+ *
+ * @return {@link SpanBuilderImpl} for connecting.
+ */
+ public SpanBuilderImpl doNotReport() {
+ this.span.setReport(false);
+ return this;
+ }
+
+ /**
+ * Collects baggage from parent. Copied from the io.opentracing implementation.
+ *
+ * @param from
+ * context to copy baggage from
+ */
+ private void withBaggageFrom(SpanContext from) {
+ Iterable> baggageItems = from.baggageItems();
+ if ((null == baggageItems) || (null == baggageItems.iterator())) {
+ return;
+ }
+
+ for (Entry baggageItem : baggageItems) {
+ baggage.put(baggageItem.getKey(), baggageItem.getValue());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanImpl start() {
+ // resolve context
+ SpanContextImpl context = SpanContextImpl.build(parent, referenceType, baggage);
+ span.setSpanContext(context);
+
+ // if startTimestamp was not specified in the builder, we use the timer
+ long nanoTime = 0;
+ if (startTimestamp <= 0) {
+ startTimestamp = timer.getCurrentTimeMicroseconds();
+ nanoTime = timer.getCurrentNanoTime();
+ }
+
+ // set start time as last operation so get more accuracy
+ span.start(startTimestamp, nanoTime);
+
+ return span;
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanContextImpl.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanContextImpl.java
new file mode 100644
index 000000000..94bdccda3
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanContextImpl.java
@@ -0,0 +1,270 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.impl;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import io.opentracing.SpanContext;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.util.RandomUtils;
+
+/**
+ * Implementation of the {@link io.opentracing.SpanContext}. Keeps information about span id, trace
+ * id and span parent id. Sampling flag is not included in the moment as it's not explicitly defined
+ * by the opentracing API.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class SpanContextImpl implements SpanContext {
+
+ /**
+ * Span id.
+ */
+ private final long id;
+
+ /**
+ * Trace id.
+ */
+ private final long traceId;
+
+ /**
+ * Parent span id.
+ */
+ private final long parentId;
+
+ /**
+ * Reference type to the parent context.
+ */
+ private final String referenceType;
+
+ /**
+ * Baggage.
+ */
+ private final Map baggage;
+
+ /**
+ * Constructor. Use build methods.
+ *
+ * @param id
+ * Unique ID of the span.
+ * @param traceId
+ * ID of the trace that span belongs to.
+ * @param parentId
+ * ID of the span's parent.
+ * @param referenceType
+ * Reference to the parent.
+ * @param baggage
+ * Additional baggage
+ */
+ public SpanContextImpl(long id, long traceId, long parentId, String referenceType, Map baggage) {
+ // ids
+ this.id = id;
+ this.traceId = traceId;
+ this.parentId = parentId;
+ this.referenceType = referenceType;
+ // baggage handling
+ if ((null != baggage) && !baggage.isEmpty()) {
+ this.baggage = new HashMap(baggage);
+ } else {
+ this.baggage = new HashMap(0, 1f);
+ }
+ }
+
+ /**
+ * Builds new {@link SpanContextImpl} as a child of given parent context. If parent context is
+ * null
then {@link #build(Map)} will be used and new trace context will be
+ * created.
+ *
+ * Passed baggage will be the baggage of this span context.
+ *
+ * @param parent
+ * Context that will be use to determine to which trace/parent new context belongs.
+ * Can be null
to denote that the new trace context should be created.
+ * @param referenceType
+ * Reference type to the parent context.
+ * @param baggage
+ * Context baggage.
+ * @return {@link SpanContextImpl}. Never null
.
+ */
+ public static SpanContextImpl build(SpanContextImpl parent, String referenceType, Map baggage) {
+ if (null == parent) {
+ return build(baggage);
+ } else {
+ long id = RandomUtils.randomLong();
+ SpanContextImpl spanContextImpl = new SpanContextImpl(id, parent.getTraceId(), parent.getId(), referenceType, baggage);
+ return spanContextImpl;
+ }
+ }
+
+ /**
+ * Builds new {@link SpanContextImpl} with new trace context and given baggage.
+ *
+ * @param baggage
+ * Context baggage.
+ * @return {@link SpanContextImpl}. Never null
.
+ */
+ public static SpanContextImpl build(Map baggage) {
+ long id = RandomUtils.randomLong();
+ return new SpanContextImpl(id, id, id, null, baggage);
+ }
+
+ /**
+ * Builds new {@link SpanContextImpl} with new trace context and no baggage.
+ *
+ * @return {@link SpanContextImpl}. Never null
.
+ */
+ public static SpanContextImpl build() {
+ long id = RandomUtils.randomLong();
+ return new SpanContextImpl(id, id, id, null, null);
+ }
+
+ /**
+ * This method is used when building context that is extracted from the propagation (like HTTP
+ * headers). In this only calling span id and trace id are passed over the network with the
+ * baggage. Returned context represents the context of the calling span.
+ *
+ * @param passedId
+ * calling span id
+ * @param passedTraceId
+ * calling span trace id
+ * @param passedBaggage
+ * passage traveling along
+ * @return Context representing the context of the calling span.
+ */
+ public static SpanContextImpl buildExtractedContext(long passedId, long passedTraceId, Map passedBaggage) {
+ return new SpanContextImpl(passedId, passedTraceId, passedId, null, passedBaggage);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterable> baggageItems() {
+ return Collections.unmodifiableMap(baggage).entrySet();
+ }
+
+ /**
+ * Sets baggage item.
+ *
+ * @param key
+ * key
+ * @param value
+ * value
+ */
+ public void setBaggageItem(String key, String value) {
+ baggage.put(key, value);
+ }
+
+ /**
+ * Gets baggage item.
+ *
+ * @param key
+ * key
+ * @return Baggage item or null
if the one does not exist.
+ */
+ public String getBaggageItem(String key) {
+ return baggage.get(key);
+ }
+
+ /**
+ * Returns reference type to the parent if the one is set.
+ *
+ * @return Returns reference type to the parent if the one is set.
+ */
+ public String getReferenceType() {
+ return this.referenceType;
+ }
+
+ /**
+ * Gets {@link #id}.
+ *
+ * @return {@link #id}
+ */
+ public long getId() {
+ return this.id;
+ }
+
+ /**
+ * Gets {@link #traceId}.
+ *
+ * @return {@link #traceId}
+ */
+ public long getTraceId() {
+ return this.traceId;
+ }
+
+ /**
+ * Gets {@link #parentId}.
+ *
+ * @return {@link #parentId}
+ */
+ public long getParentId() {
+ return this.parentId;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = (prime * result) + ((this.baggage == null) ? 0 : this.baggage.hashCode());
+ result = (prime * result) + (int) (this.id ^ (this.id >>> 32));
+ result = (prime * result) + (int) (this.parentId ^ (this.parentId >>> 32));
+ result = (prime * result) + ((this.referenceType == null) ? 0 : this.referenceType.hashCode());
+ result = (prime * result) + (int) (this.traceId ^ (this.traceId >>> 32));
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ SpanContextImpl other = (SpanContextImpl) obj;
+ if (this.baggage == null) {
+ if (other.baggage != null) {
+ return false;
+ }
+ } else if (!this.baggage.equals(other.baggage)) {
+ return false;
+ }
+ if (this.id != other.id) {
+ return false;
+ }
+ if (this.parentId != other.parentId) {
+ return false;
+ }
+ if (this.referenceType == null) {
+ if (other.referenceType != null) {
+ return false;
+ }
+ } else if (!this.referenceType.equals(other.referenceType)) {
+ return false;
+ }
+ if (this.traceId != other.traceId) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "SpanContextImpl [id=" + this.id + ", traceId=" + this.traceId + ", parentId=" + this.parentId + ", referenceType=" + this.referenceType + ", baggage=" + this.baggage + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanImpl.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanImpl.java
new file mode 100644
index 000000000..d4c90a853
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanImpl.java
@@ -0,0 +1,423 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import io.opentracing.Span;
+import io.opentracing.SpanContext;
+import io.opentracing.tag.Tags;
+import rocks.inspectit.agent.java.sdk.opentracing.Reporter;
+
+/**
+ * Implementation of the opentracing.io {@link Span}. This implementation is not thread safe, as
+ * anyway one span should be bounded to one thread.
+ *
+ * Limitations: This span implementation is ignoring the calls to the log
+ * methods as we currently don't support displaying of log events in inspectIT.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class SpanImpl implements Span {
+
+ /**
+ * Tracer.
+ */
+ private final TracerImpl tracer;
+
+ /**
+ * Operation name as per io.tracing specification.
+ */
+ private String operationName;
+
+ /**
+ * {@link SpanContext} of this span.
+ */
+ private SpanContextImpl spanContext;
+
+ /**
+ * Start time of the span. Microseconds since epoch.
+ */
+ private long startTimeMicros;
+
+ /**
+ * Start time nanos of the span. Can be used for more precise duration calculation.
+ */
+ private long startTimeNanos;
+
+ /**
+ * Duration of the span in microseconds. We keep the span duration as double to that we can keep
+ * the nano fraction when possible.
+ */
+ private double duration;
+
+ /**
+ * If the span should be reported to the {@link Reporter}.
+ */
+ private boolean report = true;
+
+ /**
+ * Tags of this span. We save all tag values as string.
+ */
+ private Map tags;
+
+ /**
+ * Default constructor. Sets only the tracer.
+ *
+ * @param tracer
+ * Tracer.
+ */
+ public SpanImpl(TracerImpl tracer) {
+ this.tracer = tracer;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanContextImpl context() {
+ return spanContext;
+ }
+
+ /**
+ * Internal start method with start time in microseconds and nano ticks.
+ *
+ * @param startTimeMicros
+ * Start time in microseconds. Must not be 0 or negative.
+ * @param startTimeNanos
+ * Current nano ticks. In case this value is not zero duration will be calculated
+ * using nano time.
+ */
+ void start(long startTimeMicros, long startTimeNanos) {
+ if (startTimeMicros <= 0) {
+ throw new IllegalArgumentException("Start time in microseconds must be provided.");
+ }
+
+ this.startTimeMicros = startTimeMicros;
+ this.startTimeNanos = startTimeNanos;
+ tracer.spanStarted(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void finish() {
+ if (startTimeNanos != 0) {
+ finishWithNanos(tracer.getTimer().getCurrentNanoTime());
+ } else {
+ finish(tracer.getTimer().getCurrentTimeMicroseconds());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void finish(long finishMicros) {
+ duration = finishMicros - startTimeMicros;
+ tracer.spanEnded(this);
+ }
+
+ /**
+ * Finishes the span with the nano duration, uses {@link #startTimeNanos} to calculate final
+ * duration.
+ *
+ * @param nanos
+ * Current nano time..
+ */
+ private void finishWithNanos(long nanos) {
+ duration = (nanos - startTimeNanos) / 1000.d;
+ tracer.spanEnded(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates call to {@link #finish()}.
+ */
+ @Override
+ public void close() {
+ finish();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Span setTag(String key, String value) {
+ return setTagInternal(key, value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Span setTag(String key, boolean value) {
+ return setTagInternal(key, String.valueOf(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Span setTag(String key, Number value) {
+ return setTagInternal(key, value.toString());
+ }
+
+ /**
+ * Internal method for span adding.
+ *
+ * @param key
+ * Span key
+ * @param value
+ * Span value
+ * @return This object
+ */
+ private Span setTagInternal(String key, String value) {
+ if (null == tags) {
+ tags = new HashMap(1, 1f);
+ }
+ tags.put(key, value);
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Not implemented by the inspectIT SDK.
+ */
+ @Override
+ public Span log(Map fields) {
+ // ignored
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Not implemented by the inspectIT SDK.
+ */
+ @Override
+ public Span log(long timestampMicroseconds, Map fields) {
+ // ignored
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Not implemented by the inspectIT SDK.
+ */
+ @Override
+ public Span log(String event) {
+ // ignored
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Not implemented by the inspectIT SDK.
+ */
+ @Override
+ public Span log(long timestampMicroseconds, String event) {
+ // ignored
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Not implemented by the inspectIT SDK.
+ *
+ * @deprecated use {@link #log(Map)}
+ */
+ @Deprecated
+ @Override
+ public Span log(String eventName, Object payload) {
+ // ignored
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Not implemented by the inspectIT SDK.
+ *
+ * @deprecated use {@link #log(Map)}
+ */
+ @Deprecated
+ @Override
+ public Span log(long timestampMicroseconds, String eventName, Object payload) {
+ // ignored
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Span setBaggageItem(String key, String value) {
+ if (null != spanContext) {
+ spanContext.setBaggageItem(key, value);
+ }
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getBaggageItem(String key) {
+ if (null != spanContext) {
+ return spanContext.getBaggageItem(key);
+ }
+ return null;
+ }
+
+ /**
+ * If span is of a type client. Only returns true
if span is explicitly declared as
+ * client.
+ *
+ * @return If span is of a type client.
+ * @see Tags#SPAN_KIND
+ */
+ public boolean isClient() {
+ if (tags != null) {
+ String kind = tags.get(Tags.SPAN_KIND.getKey());
+ return Tags.SPAN_KIND_CLIENT.equals(kind);
+ }
+ return false;
+ }
+
+ /**
+ * If span is of a type server. Only returns false
if span is explicitly declared
+ * as client.
+ *
+ * @return If span is of a type server.
+ * @see Tags#SPAN_KIND
+ */
+ public boolean isServer() {
+ return !isClient();
+ }
+
+ /**
+ * Gets {@link #operationName}.
+ *
+ * @return {@link #operationName}
+ */
+ public String getOperationName() {
+ return this.operationName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Span setOperationName(String operationName) {
+ this.operationName = operationName;
+ return this;
+ }
+
+ /**
+ * Gets {@link #startTimeMicros}.
+ *
+ * @return {@link #startTimeMicros}
+ */
+ public long getStartTimeMicros() {
+ return this.startTimeMicros;
+ }
+
+ /**
+ * Gets {@link #duration}.
+ *
+ * @return {@link #duration}
+ */
+ public double getDuration() {
+ return this.duration;
+ }
+
+ /**
+ * Gets {@link #tags}.
+ *
+ * @return {@link #tags}
+ */
+ public Map getTags() {
+ return this.tags;
+ }
+
+ /**
+ * Sets {@link #spanContext}.
+ *
+ * @param spanContext
+ * New value for {@link #spanContext}
+ */
+ void setSpanContext(SpanContextImpl spanContext) {
+ this.spanContext = spanContext;
+ }
+
+ /**
+ * Gets {@link #report}.
+ *
+ * @return {@link #report}
+ */
+ public boolean isReport() {
+ return this.report;
+ }
+
+ /**
+ * Sets {@link #report}.
+ *
+ * @param report
+ * New value for {@link #report}
+ */
+ void setReport(boolean report) {
+ this.report = report;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = (prime * result) + ((this.spanContext == null) ? 0 : this.spanContext.hashCode());
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ SpanImpl other = (SpanImpl) obj;
+ if (this.spanContext == null) {
+ if (other.spanContext != null) {
+ return false;
+ }
+ } else if (!this.spanContext.equals(other.spanContext)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "SpanImpl [spanContext=" + this.spanContext + ", operationName=" + this.operationName + ", duration=" + this.duration + ", report=" + this.report + ", tags=" + this.tags + "]";
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/TracerImpl.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/TracerImpl.java
new file mode 100644
index 000000000..09fd14fb0
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/TracerImpl.java
@@ -0,0 +1,341 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.impl;
+
+import java.util.Map;
+import java.util.Stack;
+import java.util.concurrent.ConcurrentHashMap;
+
+import io.opentracing.References;
+import io.opentracing.SpanContext;
+import io.opentracing.propagation.Format;
+import rocks.inspectit.agent.java.sdk.opentracing.ExtendedTracer;
+import rocks.inspectit.agent.java.sdk.opentracing.Reporter;
+import rocks.inspectit.agent.java.sdk.opentracing.Timer;
+import rocks.inspectit.agent.java.sdk.opentracing.TracerProvider;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.TracerLogger;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.propagation.TextMapPropagator;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.propagation.UrlEncodingPropagator;
+import rocks.inspectit.agent.java.sdk.opentracing.noop.NoopReporter;
+import rocks.inspectit.agent.java.sdk.opentracing.propagation.Propagator;
+import rocks.inspectit.agent.java.sdk.opentracing.util.SystemTimer;
+
+/**
+ * The io.opentracing tracer implementation. This tracer keeps the thread context state. Every time
+ * a span is started it will be added to the current thread span stack. Every time span is finished
+ * it will be removed from the thread stack.
+ *
+ * The tracer uses {@link Timer} for time measurement. inspectIT SDK provides a simple timer
+ * implementation ({@link SystemTimer}), as it needs to be compatible with java 6.
+ *
+ * The tracer uses {@link Reporter} for reporting finished spans. In inspectIT SDK there is an
+ * option to explicitly state that span should not be reported, as inspectIT itself adds other
+ * information to the span it creates and reports them itself. User created spans will always be
+ * reported if not explicitly stated otherwise.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class TracerImpl implements ExtendedTracer {
+
+ /**
+ * {@link TracerLogger} of this class.
+ */
+ private static final TracerLogger LOGGER = TracerLoggerWrapper.getTraceLogger(TracerImpl.class);
+
+ /**
+ * Span stack.
+ */
+ private final ThreadLocal> spanStack = new ThreadLocal>() {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected Stack initialValue() {
+ return new Stack();
+ }
+ };
+
+ /**
+ * Timer for measuring times.
+ */
+ private Timer timer;
+
+ /**
+ * Reporter to report spans to.
+ */
+ private final Reporter reporter;
+
+ /**
+ * Usable propagators.
+ */
+ private final Map, Propagator>> propagators = new ConcurrentHashMap, Propagator>>(4, 1f);
+
+ /**
+ * Initializes the tracer with {@link SystemTimer} and {@link NoopReporter}. Please use
+ * {@link TracerImpl#TracerImpl(Timer, Reporter, boolean)} for initialization with reporter of
+ * your choice.
+ */
+ public TracerImpl() {
+ this(new SystemTimer(), new NoopReporter(), false);
+ }
+
+ /**
+ * Default constructor. Timer and Reporter for this tracer must be provided.
+ *
+ * @param timer
+ * {@link Timer}
+ * @param reporter
+ * {@link Reporter}
+ * @param setToTracerProvider
+ * If this tracer should be set to the {@link TracerProvider} class for static usage.
+ */
+ public TracerImpl(Timer timer, Reporter reporter, boolean setToTracerProvider) {
+ if (null == timer) {
+ throw new IllegalArgumentException("Timer can not be null.");
+ }
+ if (null == reporter) {
+ throw new IllegalArgumentException("Reporter can not be null.");
+ }
+ this.timer = timer;
+ this.reporter = reporter;
+
+ registerDefaultPropagators();
+
+ if (setToTracerProvider) {
+ TracerProvider.set(this);
+ }
+ }
+
+ /**
+ * Registers default propagators. Users can overwrite by using
+ * {@link #registerPropagator(Format, Propagator)}.
+ */
+ private void registerDefaultPropagators() {
+ registerPropagator(Format.Builtin.TEXT_MAP, new TextMapPropagator());
+ registerPropagator(Format.Builtin.HTTP_HEADERS, new UrlEncodingPropagator());
+ }
+
+ /**
+ * Registers propagator.
+ *
+ * @param
+ * format type
+ * @param format
+ * opentracing {@link Format}
+ * @param propagator
+ * {@link Propagator}
+ */
+ @Override
+ public void registerPropagator(Format format, Propagator propagator) {
+ propagators.put(format, propagator);
+
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("New propagator registered for the format" + format.toString() + ".");
+ }
+ }
+
+ /**
+ * Builds span with no operation name.
+ *
+ * @return {@link SpanBuilder}.
+ */
+ @Override
+ public SpanBuilderImpl buildSpan() {
+ return buildSpan(null);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Note that as tracer is thread span context aware this method will automatically add reference
+ * (CHILD_OF) to the current thread span context if the one exists. If you want to manually
+ * specify reference type or want to ignore the current thread context then use
+ * {@link #buildSpan(String, String, boolean)}.
+ */
+ @Override
+ public SpanBuilderImpl buildSpan(String operationName) {
+ return buildSpan(operationName, References.CHILD_OF, true);
+ }
+
+ /**
+ * Creates {@link SpanBuilder} that optionally adds the reference to the current thread context
+ * span.
+ *
+ * @param operationName
+ * Operation name of the span.
+ * @param referenceType
+ * Reference type to the current context.
+ * @param useThreadContext
+ * If thread context should be used.
+ * @return {@link SpanBuilder}.
+ */
+ @Override
+ public SpanBuilderImpl buildSpan(String operationName, String referenceType, boolean useThreadContext) {
+ SpanBuilderImpl spanBuilder = new SpanBuilderImpl(this, operationName);
+
+ if (useThreadContext) {
+ // check the current thread context
+ SpanContextImpl threadContext = getCurrentContext();
+ if (threadContext != null) {
+ spanBuilder.addReference(referenceType, threadContext);
+ }
+ }
+
+ return spanBuilder;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public void inject(SpanContext spanContext, Format format, C carrier) {
+ if ((format == null) || (carrier == null)) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Context can not be injected, both format and carrier must be provided.");
+ }
+ return;
+ }
+
+ if (spanContext instanceof SpanContextImpl) {
+ Propagator propagator = (Propagator) propagators.get(format);
+ if (null != propagator) {
+ propagator.inject((SpanContextImpl) spanContext, carrier);
+ } else {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Context can not be injected, propagator does not exists for the format " + format.toString() + ".");
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public SpanContext extract(Format format, C carrier) {
+ if ((format == null) || (carrier == null)) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Context can not be extracted, both format and carrier must be provided.");
+ }
+ return null;
+ }
+
+ Propagator propagator = (Propagator) propagators.get(format);
+ if (null != propagator) {
+ SpanContextImpl context = propagator.extract(carrier);
+
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Context extracted: " + context);
+ }
+
+ return context;
+ } else {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Context can not be extracted, propagator does not exists for the format " + format.toString() + ".");
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Returns current context.
+ *
+ * @return Returns current context.
+ */
+ public SpanContextImpl getCurrentContext() {
+ Stack stack = spanStack.get();
+ if (!stack.isEmpty()) {
+ return stack.peek().context();
+ }
+ return null;
+ }
+
+ /**
+ * Returns if the thread context exists.
+ *
+ * @return Returns if the thread context exists.
+ */
+ public boolean isCurrentContextExisting() {
+ return !spanStack.get().isEmpty();
+ }
+
+ /**
+ * Reports the span to the tracer once the span is started.
+ *
+ * @param span
+ * Span.
+ */
+ void spanStarted(SpanImpl span) {
+ if (null == span) {
+ return;
+ }
+
+ spanStack.get().push(span);
+
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Span started " + span);
+ }
+ }
+
+ /**
+ * Reports the span to the tracer once the span is finished.
+ *
+ * @param span
+ * Span.
+ */
+ void spanEnded(SpanImpl span) {
+ if (null == span) {
+ return;
+ }
+
+ // check if we have the span in the stack
+ Stack stack = spanStack.get();
+ if (stack.contains(span)) {
+ // if so clear the stack until we reach it
+ // it should be the first one, but just for safety
+ // (users might forget to finish spans or could finish them in wrong order)
+ SpanImpl removed = stack.pop();
+ boolean wrongEndOrder = false;
+ while (!stack.isEmpty() && !removed.equals(span)) {
+ wrongEndOrder = true;
+ stack.pop();
+ }
+
+ if (wrongEndOrder && LOGGER.isWarnEnabled()) {
+ LOGGER.warn("Finishing of spans is not done in starting order, span " + span.toString() + " is not the last started one by current thread. Thread context state can be affected.");
+ }
+
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Span finished " + span);
+ }
+ }
+
+ // check if we need to report the span
+ if (span.isReport()) {
+ reporter.report(span);
+ }
+ }
+
+ /**
+ * Gets {@link #timer}.
+ *
+ * @return {@link #timer}
+ */
+ Timer getTimer() {
+ return this.timer;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setTimer(Timer timer) {
+ if (null == timer) {
+ throw new IllegalArgumentException("Timer must not be null.");
+ }
+ this.timer = timer;
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/TracerLoggerWrapper.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/TracerLoggerWrapper.java
new file mode 100644
index 000000000..cd74e7cc4
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/TracerLoggerWrapper.java
@@ -0,0 +1,61 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.impl;
+
+import rocks.inspectit.agent.java.sdk.opentracing.internal.TracerLogger;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.TracerLoggerProvider;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.noop.NoopLogger;
+
+/**
+ * Wraps the {@link TracerLoggerProvider} that will be used to provide {@link TracerLogger}.
+ *
+ * In order to not introduce any logging dependencies to the SDK project, this class represents a
+ * way to bridge the logging framework used in inspectIT to the SDK.
+ *
+ * @author Ivan Senic
+ *
+ */
+public final class TracerLoggerWrapper {
+
+ /**
+ * By default we use provider that returns the {@link NoopLogger}. If inspectIT agent is used
+ * then the provider will be overwritten with the one providing real logger instances.
+ */
+ private static TracerLoggerProvider provider = new TracerLoggerProvider() {
+
+ @Override
+ public TracerLogger getTraceLogger(Class> clazz) {
+ return NoopLogger.INSTANCE;
+ }
+ };
+
+ /**
+ * Private constructor, only static methods.
+ */
+ private TracerLoggerWrapper() {
+ }
+
+ /**
+ * Returns proper {@link TracerLogger} for the given class. If inspectIT agent is not used, then
+ * returned logger will be {@link NoopLogger}. Otherwise, inspectIT will do the bridging to the
+ * logging framework used in inspectIT (currently slf4j).
+ *
+ * @param clazz
+ * Class to get logger for.
+ * @return {@link TracerLogger}
+ */
+ public static TracerLogger getTraceLogger(Class> clazz) {
+ return provider.getTraceLogger(clazz);
+ }
+
+ /**
+ * Sets {@link #provider}. Should be called only by inspectIT SDK classes.
+ *
+ * @param provider
+ * New value for {@link #provider}
+ */
+ public static void setProvider(TracerLoggerProvider provider) {
+ if (null != provider) {
+ TracerLoggerWrapper.provider = provider;
+ }
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/noop/NoopExtendedTracerImpl.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/noop/NoopExtendedTracerImpl.java
new file mode 100644
index 000000000..1a1302f11
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/noop/NoopExtendedTracerImpl.java
@@ -0,0 +1,90 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.noop;
+
+import io.opentracing.NoopTracer;
+import io.opentracing.NoopTracerFactory;
+import io.opentracing.SpanContext;
+import io.opentracing.propagation.Format;
+import rocks.inspectit.agent.java.sdk.opentracing.ExtendedTracer;
+import rocks.inspectit.agent.java.sdk.opentracing.Timer;
+import rocks.inspectit.agent.java.sdk.opentracing.propagation.Propagator;
+
+/**
+ * No-operation tracer that implement the {@link ExtendedTracer} interface. Simply delegates calls
+ * to the {@link NoopTracer} of the opentracing.io.
+ *
+ * @author Ivan Senic
+ *
+ */
+public final class NoopExtendedTracerImpl implements ExtendedTracer, NoopTracer {
+
+ /**
+ * Instance for usage.
+ */
+ public static final NoopExtendedTracerImpl INSTANCE = new NoopExtendedTracerImpl();
+
+ /**
+ * The noop tracer from the opentracing.io.
+ */
+ private static final NoopTracer DEFAULT_NOOP_TRACER = NoopTracerFactory.create();
+
+ /**
+ * Private constructor, use {@link #INSTANCE}.
+ */
+ private NoopExtendedTracerImpl() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanBuilder buildSpan(String operationName) {
+ return DEFAULT_NOOP_TRACER.buildSpan(operationName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void inject(SpanContext spanContext, Format format, C carrier) {
+ DEFAULT_NOOP_TRACER.inject(spanContext, format, carrier);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanContext extract(Format format, C carrier) {
+ return DEFAULT_NOOP_TRACER.extract(format, carrier);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void registerPropagator(Format format, Propagator propagator) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setTimer(Timer timer) throws IllegalArgumentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanBuilder buildSpan() {
+ return DEFAULT_NOOP_TRACER.buildSpan(null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanBuilder buildSpan(String operationName, String referenceType, boolean useThreadContext) {
+ return DEFAULT_NOOP_TRACER.buildSpan(operationName);
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/noop/NoopLogger.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/noop/NoopLogger.java
new file mode 100644
index 000000000..fc19f8716
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/noop/NoopLogger.java
@@ -0,0 +1,99 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.noop;
+
+import rocks.inspectit.agent.java.sdk.opentracing.internal.TracerLogger;
+
+/**
+ * No-operation logger that implement the {@link TracerLogger} interface. Simply ignores all the log
+ * calls.
+ *
+ * @author Ivan Senic
+ *
+ */
+public final class NoopLogger implements TracerLogger {
+
+ /**
+ * Instance for usage.
+ */
+ public static final NoopLogger INSTANCE = new NoopLogger();
+
+ /**
+ * Private, use {@link #INSTANCE}.
+ */
+ private NoopLogger() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isInfoEnabled() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void info(String message) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isDebugEnabled() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void debug(String message) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isWarnEnabled() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void warn(String message) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void warn(String message, Throwable t) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isErrorEnabled() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void error(String message) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void error(String message, Throwable t) {
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/package-info.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/package-info.java
new file mode 100644
index 000000000..55b31bb4e
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * Please note that classes in this package and all sub-packages and for SDK internal purposes and
+ * should not be used by applications directly.
+ *
+ * @author Ivan Senic
+ */
+package rocks.inspectit.agent.java.sdk.opentracing.internal;
\ No newline at end of file
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/propagation/TextMapPropagator.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/propagation/TextMapPropagator.java
new file mode 100644
index 000000000..78b26b534
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/propagation/TextMapPropagator.java
@@ -0,0 +1,33 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.propagation;
+
+import java.util.Map.Entry;
+
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.sdk.opentracing.propagation.AbstractPropagator;
+import rocks.inspectit.agent.java.sdk.opentracing.propagation.Propagator;
+
+/**
+ * Simple {@link Propagator} for the {@link TextMap}. Only delegates to the {@link TextMap}.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class TextMapPropagator extends AbstractPropagator {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void injectBaggage(TextMap carrier, String key, String value) {
+ carrier.put(key, value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected Iterable> extractBaggage(TextMap carrier) {
+ return carrier;
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/propagation/UrlEncodingPropagator.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/propagation/UrlEncodingPropagator.java
new file mode 100644
index 000000000..2a954d4e5
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/propagation/UrlEncodingPropagator.java
@@ -0,0 +1,60 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.propagation;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.sdk.opentracing.propagation.AbstractPropagator;
+
+/**
+ * Propagator that tries to encode the values using {@link URLEncoder} and {@link URLDecoder}. Uses
+ * {@value #UTF_8} encoding.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class UrlEncodingPropagator extends AbstractPropagator {
+
+ /**
+ * Constant.
+ */
+ private static final String UTF_8 = "UTF-8";
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void injectBaggage(TextMap carrier, String key, String value) {
+ try {
+ carrier.put(key, URLEncoder.encode(value, UTF_8));
+ } catch (UnsupportedEncodingException e) {
+ carrier.put(key, value);
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected Iterable> extractBaggage(TextMap carrier) {
+ if ((null == carrier) || (null == carrier.iterator())) {
+ return null;
+ }
+
+ Map map = new HashMap();
+ for (Entry entry : carrier) {
+ try {
+ map.put(entry.getKey(), URLDecoder.decode(entry.getValue(), UTF_8));
+ } catch (UnsupportedEncodingException e) {
+ map.put(entry.getKey(), entry.getValue());
+ }
+ }
+ return map.entrySet();
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/util/ConversionUtils.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/util/ConversionUtils.java
new file mode 100644
index 000000000..5d6c78103
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/util/ConversionUtils.java
@@ -0,0 +1,45 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.util;
+
+/**
+ * Conversion utils we need for our tracing.
+ *
+ * @author Ivan Senic
+ *
+ */
+public final class ConversionUtils {
+
+ /**
+ * Private constructor.
+ */
+ private ConversionUtils() {
+ };
+
+ /**
+ * Parses a hexadecimal string. If passed string is null
, then 0
will
+ * be returned.
+ *
+ * @param s
+ * String to parse.
+ * @return parsed long value or 0
if given string is null
+ * @see Long#parseLong(String, int)
+ */
+ public static long parseHexStringSafe(String s) {
+ if (null == s) {
+ return 0;
+ }
+ return Long.parseLong(s, 16);
+ }
+
+ /**
+ * Converts long to hexadecimal string.
+ *
+ * @param l
+ * value
+ * @return hexadecimal representation of given long
+ *
+ * @see Long#toHexString(long)
+ */
+ public static String toHexString(long l) {
+ return Long.toString(l, 16);
+ }
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/util/RandomUtils.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/util/RandomUtils.java
new file mode 100644
index 000000000..886b172b0
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/internal/util/RandomUtils.java
@@ -0,0 +1,89 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.util;
+
+import java.lang.reflect.Field;
+import java.util.Random;
+
+import rocks.inspectit.agent.java.sdk.opentracing.internal.TracerLogger;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.TracerLoggerWrapper;
+
+/**
+ * Helper class for generating random numbers needed for the tracing. Currently works with
+ * {@link java.util.concurrent.ThreadLocalRandom} if java version is 1.7 and higher, as it's
+ * expected that there is contention when creating ids. If we are in Java 6 we'll use normal random
+ * that is thread-safe but can be slower.
+ *
+ * @author Ivan Senic
+ *
+ */
+@SuppressWarnings("unchecked")
+public final class RandomUtils {
+
+ /**
+ * {@link TracerLogger} of this class.
+ */
+ private static final TracerLogger LOGGER = TracerLoggerWrapper.getTraceLogger(RandomUtils.class);
+
+ /**
+ * Random to use.
+ */
+ private static final Random RANDOM = new Random();
+
+ /**
+ * LocalRandom field in the java.util.concurrent.ThreadLocalRandom class.
+ */
+ private static ThreadLocal threadLocalRandomLocal;
+
+ static {
+ // try to load thread local random
+ try {
+ Class> clazz = Class.forName("java.util.concurrent.ThreadLocalRandom");
+ Field field = clazz.getDeclaredField("localRandom");
+ field.setAccessible(true);
+ threadLocalRandomLocal = (ThreadLocal) field.get(null);
+ } catch (Exception e) {
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("ThreadLocalRandom is not available. Using " + RANDOM.getClass().getSimpleName() + " for generating random numbers.");
+ }
+ }
+ }
+
+ /**
+ * Private constructor.
+ */
+ private RandomUtils() {
+ }
+
+ /**
+ * Returns the random long number. Unlike {@link Random#nextLong()} method, this utility method
+ * may return all possible long values (64-bit spread, compared to 48-bit spread defined by the
+ * {@link Random} interface).
+ *
+ * @return Random long number, based on the {@link Random} returned by the {@link #getRandom()}.
+ */
+ public static long randomLong() {
+ byte[] randomBytes = new byte[8];
+ getRandom().nextBytes(randomBytes);
+ //@formatter:off
+ return ((randomBytes[0] & 0xffL) << 56)
+ | ((randomBytes[1] & 0xffL) << 48)
+ | ((randomBytes[2] & 0xffL) << 40)
+ | ((randomBytes[3] & 0xffL) << 32)
+ | ((randomBytes[4] & 0xffL) << 24)
+ | ((randomBytes[5] & 0xffL) << 16)
+ | ((randomBytes[6] & 0xffL) << 8)
+ | (randomBytes[7] & 0xffL);
+ //@formatter:on
+ }
+
+ /**
+ * Returns random to use when generating random ids.
+ *
+ * @return Returns random to use when generating random ids.
+ */
+ private static Random getRandom() {
+ if (null != threadLocalRandomLocal) {
+ return threadLocalRandomLocal.get();
+ }
+ return RANDOM;
+ }
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/noop/NoopReporter.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/noop/NoopReporter.java
new file mode 100644
index 000000000..ed4970a94
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/noop/NoopReporter.java
@@ -0,0 +1,22 @@
+package rocks.inspectit.agent.java.sdk.opentracing.noop;
+
+import rocks.inspectit.agent.java.sdk.opentracing.Reporter;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanImpl;
+
+/**
+ * Noop {@link Reporter} that does nothing when spans are reported.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class NoopReporter implements Reporter {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void report(SpanImpl span) {
+ // does nothing
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/package-info.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/package-info.java
new file mode 100644
index 000000000..eaa4841b4
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * Contains inspectIT implementation of the opentracing.io standard.
+ *
+ * Tracer can be obtained by calling one of the get
methods in the
+ * {@link TracerProvider}. If the inspectIT agent is running with the application where
+ * {@link TracerProvider} is used, the get
methods will always returned correctly
+ * initialized {@link ExtendedTracer}. If the inspectIT is not running, then the caller can control
+ * if the returned tracer is noop tracer or null
.
+ *
+ * It's up to user to decide to code against the io.opentracing interfaces or use directly
+ * {@link ExtendedTracer} which provides some additional control.
+ *
+ * Typical usage:
+ *
+ *
+ * Tracer tracer = TracerProvider.get();
+ * Span span = tracer.buildSpan().withTag("key", "value").start();
+ * ...
+ * span.finish();
+ *
+ *
+ *
+ * @author Ivan Senic
+ *
+ */
+package rocks.inspectit.agent.java.sdk.opentracing;
\ No newline at end of file
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/propagation/AbstractPropagator.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/propagation/AbstractPropagator.java
new file mode 100644
index 000000000..f9e1325c8
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/propagation/AbstractPropagator.java
@@ -0,0 +1,131 @@
+package rocks.inspectit.agent.java.sdk.opentracing.propagation;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import rocks.inspectit.agent.java.sdk.opentracing.internal.TracerLogger;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.constants.PropagationConstants;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanContextImpl;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.TracerLoggerWrapper;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.util.ConversionUtils;
+
+/**
+ * Abstract {@link Propagator} that knows what is injected and extracted from the carrier.
+ * Implementing classes must only provide {@link #injectBaggage(Object, String, String)} and
+ * {@link #extractBaggage(Object)} methods, where they can deal with any needed operations as
+ * encoding for example.
+ *
+ * @param
+ * type of carrier
+ * @author Ivan Senic
+ *
+ */
+public abstract class AbstractPropagator implements Propagator {
+
+ /**
+ * Logger of this class.
+ */
+ private static final TracerLogger LOGGER = TracerLoggerWrapper.getTraceLogger(AbstractPropagator.class);
+
+ /**
+ * Injects one baggage item to the carrier. Sub-class is responsible of providing exactly this
+ * key/value once {@link #extractBaggage(Object)} is called, but can wrap the key/value in what
+ * ever they want.
+ *
+ * @param carrier
+ * carrier
+ * @param key
+ * key
+ * @param value
+ * value
+ */
+ protected abstract void injectBaggage(C carrier, String key, String value);
+
+ /**
+ * Gets all baggage (not only tracing related) from the carrier. For example the HTTP carrier
+ * would here provide all headers. Subclasses can deal here with additional operations as
+ * encoding.
+ *
+ * @param carrier
+ * carrier
+ * @return Itarbale
+ */
+ protected abstract Iterable> extractBaggage(C carrier);
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void inject(SpanContextImpl spanContext, C carrier) {
+ if (null == spanContext) {
+ return;
+ }
+
+ injectBaggage(carrier, PropagationConstants.SPAN_ID, ConversionUtils.toHexString(spanContext.getId()));
+ injectBaggage(carrier, PropagationConstants.TRACE_ID, ConversionUtils.toHexString(spanContext.getTraceId()));
+ Iterable> baggageItems = spanContext.baggageItems();
+ if (null != baggageItems) {
+ for (Map.Entry e : baggageItems) {
+ injectBaggage(carrier, PropagationConstants.INSPECTIT_BAGGAGE_PREFIX + e.getKey(), e.getValue());
+ }
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanContextImpl extract(C carrier) {
+ Iterable> iterable = extractBaggage(carrier);
+ if ((null == iterable) || (null == iterable.iterator())) {
+ return null;
+ }
+
+ Map passedBaggage = new HashMap();
+ String idFromBaggage = null;
+ String traceIdFromBaggage = null;
+ // iterate over the baggage
+ for (Entry e : iterable) {
+ String key = e.getKey();
+ if (PropagationConstants.SPAN_ID.equals(key)) {
+ idFromBaggage = e.getValue();
+ } else if (PropagationConstants.TRACE_ID.equals(key)) {
+ traceIdFromBaggage = e.getValue();
+ } else if (key.startsWith(PropagationConstants.INSPECTIT_BAGGAGE_PREFIX)) {
+ String realKey = key.substring(PropagationConstants.INSPECTIT_BAGGAGE_PREFIX.length());
+ passedBaggage.put(realKey, e.getValue());
+ }
+ }
+
+ // at least span id and trace id are needed, if they are not passed return null
+ if (notEmpty(idFromBaggage) && notEmpty(traceIdFromBaggage)) {
+ try {
+ long id = ConversionUtils.parseHexStringSafe(idFromBaggage);
+ long traceId = ConversionUtils.parseHexStringSafe(traceIdFromBaggage);
+ return SpanContextImpl.buildExtractedContext(id, traceId, passedBaggage);
+ } catch (NumberFormatException e) {
+ if (LOGGER.isWarnEnabled()) {
+ LOGGER.warn("Failed converting span and trace id. These are expected in the hexadecimal format, but were " + idFromBaggage + " and " + traceIdFromBaggage + ".", e);
+ }
+ // ids were not parsable
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Small utility to check if string is not empty.
+ *
+ * @param s
+ * String
+ * @return If string is not null and has at least 1 char
+ */
+ private boolean notEmpty(String s) {
+ return (null != s) && !s.isEmpty();
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/propagation/Propagator.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/propagation/Propagator.java
new file mode 100644
index 000000000..3e5d99a90
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/propagation/Propagator.java
@@ -0,0 +1,33 @@
+package rocks.inspectit.agent.java.sdk.opentracing.propagation;
+
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanContextImpl;
+
+/**
+ * Propagator interface to help with the injection and extraction of the span context.
+ *
+ * @param
+ * type of carrier
+ * @author Ivan Senic
+ *
+ */
+public interface Propagator {
+
+ /**
+ * Injects the span context to the carrier.
+ *
+ * @param spanContext
+ * context
+ * @param carrier
+ * carrier
+ */
+ void inject(SpanContextImpl spanContext, C carrier);
+
+ /**
+ * Extract the span context from the carrier.
+ *
+ * @param carrier
+ * carrier
+ * @return span context
+ */
+ SpanContextImpl extract(C carrier);
+}
diff --git a/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/util/SystemTimer.java b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/util/SystemTimer.java
new file mode 100644
index 000000000..756ed9788
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/main/java/rocks/inspectit/agent/java/sdk/opentracing/util/SystemTimer.java
@@ -0,0 +1,29 @@
+package rocks.inspectit.agent.java.sdk.opentracing.util;
+
+import rocks.inspectit.agent.java.sdk.opentracing.Timer;
+
+/**
+ * {@link Timer} implementation that uses {@link java.lang.System} for timing.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class SystemTimer implements Timer {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long getCurrentTimeMicroseconds() {
+ return System.currentTimeMillis() * 1000L;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long getCurrentNanoTime() {
+ return System.nanoTime();
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanBuilderImplTest.java b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanBuilderImplTest.java
new file mode 100644
index 000000000..aa7fe6a68
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanBuilderImplTest.java
@@ -0,0 +1,260 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.impl;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Random;
+
+import org.mockito.Mock;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import io.opentracing.References;
+import io.opentracing.Span;
+import io.opentracing.SpanContext;
+import rocks.inspectit.agent.java.sdk.opentracing.Timer;
+import rocks.inspectit.shared.all.testbase.TestBase;
+
+/**
+ * @author Ivan Senic
+ *
+ */
+@SuppressWarnings("PMD")
+public class SpanBuilderImplTest extends TestBase {
+
+ @Mock
+ Timer timer;
+
+ @Mock
+ TracerImpl tracer;
+
+ @BeforeMethod
+ public void init() {
+ when(tracer.getTimer()).thenReturn(timer);
+ }
+
+ public static class Start extends SpanBuilderImplTest {
+
+ Random random = new Random();
+
+ @Test
+ public void base() {
+ String op = "operation";
+ long currentTime = System.currentTimeMillis();
+ long nano1 = random.nextLong();
+ long nano2 = nano1 + random.nextInt(5000);
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(currentTime);
+ when(timer.getCurrentNanoTime()).thenReturn(nano1).thenReturn(nano2);
+ SpanBuilderImpl builder = new SpanBuilderImpl(tracer, op);
+
+ SpanImpl span = builder.start();
+
+ assertThat(span.getOperationName(), is(op));
+ assertThat(span.getStartTimeMicros(), is(currentTime));
+ assertThat(span.context().getId(), is(span.context().getParentId()));
+ assertThat(span.context().getReferenceType(), is(nullValue()));
+ // for duration finish
+ span.finish();
+ assertThat(span.getDuration(), is((nano2 - nano1) / 1000.0d));
+ // verify tracer passed
+ verify(tracer).spanStarted(span);
+ verify(tracer).spanEnded(span);
+ verify(tracer, atLeastOnce()).getTimer();
+ verify(timer).getCurrentTimeMicroseconds();
+ verify(timer, times(2)).getCurrentNanoTime();
+ verifyNoMoreInteractions(tracer, timer);
+ }
+
+ @Test
+ public void operationNull() {
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(System.currentTimeMillis());
+ SpanBuilderImpl builder = new SpanBuilderImpl(tracer, null);
+
+ SpanImpl span = builder.start();
+
+ assertThat(span.getOperationName(), is(nullValue()));
+ }
+
+ @Test
+ public void parent() {
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(System.currentTimeMillis());
+ SpanContextImpl parent = new SpanContextImpl(1, 2, 3, null, Collections. singletonMap("key", "value"));
+ SpanBuilderImpl builder = new SpanBuilderImpl(tracer, null).addReference(References.FOLLOWS_FROM, parent);
+
+ SpanImpl span = builder.start();
+
+ assertThat(span.context().getParentId(), is(parent.getId()));
+ assertThat(span.context().getTraceId(), is(parent.getTraceId()));
+ assertThat(span.context().getReferenceType(), is(References.FOLLOWS_FROM));
+ Map contextBaggage = mapFromEntryIterator(span.context().baggageItems());
+ assertThat(contextBaggage.size(), is(1));
+ assertThat(contextBaggage, hasEntry("key", "value"));
+ // assert baggage of the builder as well
+ Map builderBaggage = mapFromEntryIterator(builder.baggageItems());
+ assertThat(builderBaggage.size(), is(1));
+ assertThat(builderBaggage, hasEntry("key", "value"));
+ }
+
+ @Test
+ public void twoParents() {
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(System.currentTimeMillis());
+ SpanContextImpl parent1 = new SpanContextImpl(1, 2, 3, null, Collections. singletonMap("key1", "value1"));
+ SpanContextImpl parent2 = new SpanContextImpl(4, 5, 6, null, Collections. singletonMap("key2", "value2"));
+ SpanBuilderImpl builder = new SpanBuilderImpl(tracer, null).asChildOf(parent1).asChildOf(parent2);
+
+ SpanImpl span = builder.start();
+
+ // first parent is trace responsible
+ assertThat(span.context().getParentId(), is(parent1.getId()));
+ assertThat(span.context().getTraceId(), is(parent1.getTraceId()));
+ assertThat(span.context().getReferenceType(), is(References.CHILD_OF));
+ // baggage is taken from both parents
+ Map contextBaggage = mapFromEntryIterator(span.context().baggageItems());
+ assertThat(contextBaggage.size(), is(2));
+ assertThat(contextBaggage, hasEntry("key1", "value1"));
+ assertThat(contextBaggage, hasEntry("key2", "value2"));
+ // assert baggage of the builder as well
+ Map builderBaggage = mapFromEntryIterator(builder.baggageItems());
+ assertThat(builderBaggage.size(), is(2));
+ assertThat(builderBaggage, hasEntry("key1", "value1"));
+ assertThat(builderBaggage, hasEntry("key2", "value2"));
+ }
+
+ @Test
+ public void twoParentsOneNotOurImpl() {
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(System.currentTimeMillis());
+ SpanContext parent1 = mock(SpanContext.class);
+ SpanContextImpl parent2 = new SpanContextImpl(4, 5, 6, null, Collections. singletonMap("key2", "value2"));
+ SpanBuilderImpl builder = new SpanBuilderImpl(tracer, null).asChildOf(parent1).asChildOf(parent2);
+
+ SpanImpl span = builder.start();
+
+ // first parent is trace responsible
+ assertThat(span.context().getParentId(), is(parent2.getId()));
+ assertThat(span.context().getTraceId(), is(parent2.getTraceId()));
+ assertThat(span.context().getReferenceType(), is(References.CHILD_OF));
+ // baggage is taken from both parents
+ Map contextBaggage = mapFromEntryIterator(span.context().baggageItems());
+ assertThat(contextBaggage.size(), is(1));
+ assertThat(contextBaggage, hasEntry("key2", "value2"));
+ // assert baggage of the builder as well
+ Map builderBaggage = mapFromEntryIterator(builder.baggageItems());
+ assertThat(builderBaggage.size(), is(1));
+ assertThat(builderBaggage, hasEntry("key2", "value2"));
+ }
+
+ @Test
+ public void parentNull() {
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(System.currentTimeMillis());
+ Span parent = null;
+ SpanBuilderImpl builder = new SpanBuilderImpl(tracer, null).asChildOf(parent);
+
+ SpanImpl span = builder.start();
+
+ assertThat(span.context().getId(), is(span.context().getParentId()));
+ assertThat(span.context().getReferenceType(), is(nullValue()));
+ }
+
+ @Test
+ public void parentContextNull() {
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(System.currentTimeMillis());
+ SpanContextImpl parent = null;
+ SpanBuilderImpl builder = new SpanBuilderImpl(tracer, null).asChildOf(parent);
+
+ SpanImpl span = builder.start();
+
+ assertThat(span.context().getId(), is(span.context().getParentId()));
+ assertThat(span.context().getReferenceType(), is(nullValue()));
+ }
+
+ @Test
+ public void parentWrongReference() {
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(System.currentTimeMillis());
+ SpanContextImpl parent = SpanContextImpl.build();
+ parent.setBaggageItem("key", "value");
+ SpanBuilderImpl builder = new SpanBuilderImpl(tracer, null).addReference("some_reference", parent);
+
+ SpanImpl span = builder.start();
+
+ assertThat(span.context().getId(), is(span.context().getParentId()));
+ assertThat(span.context().getReferenceType(), is(nullValue()));
+ assertThat(span.context().getBaggageItem("key"), is("value"));
+ }
+
+ @Test
+ public void noReport() {
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(System.currentTimeMillis());
+ SpanBuilderImpl builder = new SpanBuilderImpl(tracer, null).doNotReport();
+
+ SpanImpl span = builder.start();
+
+ assertThat(span.isReport(), is(false));
+ }
+
+ @Test
+ public void selfStartTime() {
+ long micros = 1422l;
+ SpanBuilderImpl builder = new SpanBuilderImpl(tracer, null).withStartTimestamp(micros);
+
+ SpanImpl span = builder.start();
+
+ assertThat(span.getStartTimeMicros(), is(micros));
+ verifyZeroInteractions(timer);
+ }
+
+ @Test
+ public void booleanTag() {
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(System.currentTimeMillis());
+ SpanBuilderImpl builder = new SpanBuilderImpl(tracer, null).withTag("key", false);
+
+ SpanImpl span = builder.start();
+
+ assertThat(span.getTags().size(), is(1));
+ assertThat(span.getTags(), hasEntry("key", String.valueOf(false)));
+ }
+
+ @Test
+ public void numberTag() {
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(System.currentTimeMillis());
+ SpanBuilderImpl builder = new SpanBuilderImpl(tracer, null).withTag("key", 5l);
+
+ SpanImpl span = builder.start();
+
+ assertThat(span.getTags().size(), is(1));
+ assertThat(span.getTags(), hasEntry("key", String.valueOf(5L)));
+ }
+
+ @Test
+ public void stringTag() {
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(System.currentTimeMillis());
+ SpanBuilderImpl builder = new SpanBuilderImpl(tracer, null).withTag("key", "value");
+
+ SpanImpl span = builder.start();
+
+ assertThat(span.getTags().size(), is(1));
+ assertThat(span.getTags(), hasEntry("key", "value"));
+ }
+ }
+
+ private static Map mapFromEntryIterator(Iterable> i) {
+ Map map = new HashMap();
+ for (Entry e : i) {
+ map.put(e.getKey(), e.getValue());
+ }
+ return map;
+
+ }
+}
diff --git a/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanContextImplTest.java b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanContextImplTest.java
new file mode 100644
index 000000000..2217bb716
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanContextImplTest.java
@@ -0,0 +1,123 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.impl;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+
+import java.util.Collections;
+import java.util.Map.Entry;
+
+import org.testng.annotations.Test;
+
+import io.opentracing.References;
+
+/**
+ * @author Ivan Senic
+ *
+ */
+@SuppressWarnings("PMD")
+public class SpanContextImplTest {
+
+ public static class Build extends SpanContextImplTest {
+
+ @Test
+ public void rootContext() {
+ SpanContextImpl context = SpanContextImpl.build();
+
+ assertThat(context.getTraceId(), is(context.getId()));
+ assertThat(context.getParentId(), is(context.getId()));
+ assertThat(context.baggageItems().iterator().hasNext(), is(false));
+ }
+
+ @Test
+ public void rootContextWithBaggage() {
+ SpanContextImpl context = SpanContextImpl.build(Collections.singletonMap("k", "v"));
+
+ assertThat(context.getTraceId(), is(context.getId()));
+ assertThat(context.getParentId(), is(context.getId()));
+ assertThat(context.baggageItems().iterator().hasNext(), is(true));
+ Entry entry = context.baggageItems().iterator().next();
+ assertThat(entry.getKey(), is("k"));
+ assertThat(entry.getValue(), is("v"));
+ }
+
+ @Test
+ public void rootContextWithBaggageNull() {
+ SpanContextImpl context = SpanContextImpl.build(null);
+
+ assertThat(context.getTraceId(), is(context.getId()));
+ assertThat(context.getParentId(), is(context.getId()));
+ assertThat(context.baggageItems().iterator().hasNext(), is(false));
+ }
+
+ @Test
+ public void childContext() {
+ SpanContextImpl parent = SpanContextImpl.build();
+
+ SpanContextImpl context = SpanContextImpl.build(parent, References.FOLLOWS_FROM, Collections. emptyMap());
+
+ assertThat(context.getTraceId(), is(parent.getTraceId()));
+ assertThat(context.getParentId(), is(parent.getId()));
+ assertThat(context.getId(), is(not(parent.getId())));
+ assertThat(context.getId(), is(not(parent.getTraceId())));
+ assertThat(context.getId(), is(not(parent.getParentId())));
+ assertThat(context.getReferenceType(), is(References.FOLLOWS_FROM));
+ assertThat(context.baggageItems().iterator().hasNext(), is(false));
+ }
+
+ @Test
+ public void childContextWithBaggage() {
+ SpanContextImpl parent = SpanContextImpl.build();
+
+ SpanContextImpl context = SpanContextImpl.build(parent, References.CHILD_OF, Collections.singletonMap("k", "v"));
+
+ assertThat(context.getTraceId(), is(parent.getTraceId()));
+ assertThat(context.getParentId(), is(parent.getId()));
+ assertThat(context.getId(), is(not(parent.getId())));
+ assertThat(context.getId(), is(not(parent.getTraceId())));
+ assertThat(context.getId(), is(not(parent.getParentId())));
+ assertThat(context.getReferenceType(), is(References.CHILD_OF));
+ assertThat(context.baggageItems().iterator().hasNext(), is(true));
+ Entry entry = context.baggageItems().iterator().next();
+ assertThat(entry.getKey(), is("k"));
+ assertThat(entry.getValue(), is("v"));
+ }
+
+ @Test
+ public void childContextReferenceNull() {
+ SpanContextImpl parent = SpanContextImpl.build();
+
+ SpanContextImpl context = SpanContextImpl.build(parent, null, Collections. emptyMap());
+
+ assertThat(context.getReferenceType(), is(nullValue()));
+ }
+
+ @Test
+ public void childContextParentNull() {
+ SpanContextImpl context = SpanContextImpl.build(null, References.CHILD_OF, Collections. emptyMap());
+
+ assertThat(context.getTraceId(), is(context.getId()));
+ assertThat(context.getParentId(), is(context.getId()));
+ assertThat(context.baggageItems().iterator().hasNext(), is(false));
+ assertThat(context.getReferenceType(), is(nullValue()));
+ }
+
+ @Test
+ public void extractedContext() {
+ long id = 1;
+ long traceId = 2;
+
+ SpanContextImpl context = SpanContextImpl.buildExtractedContext(id, traceId, Collections.singletonMap("k", "v"));
+
+ assertThat(context.getTraceId(), is(traceId));
+ assertThat(context.getId(), is(id));
+ assertThat(context.getReferenceType(), is(nullValue()));
+ assertThat(context.baggageItems().iterator().hasNext(), is(true));
+ Entry entry = context.baggageItems().iterator().next();
+ assertThat(entry.getKey(), is("k"));
+ assertThat(entry.getValue(), is("v"));
+ }
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanImplTest.java b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanImplTest.java
new file mode 100644
index 000000000..f32042cd8
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/SpanImplTest.java
@@ -0,0 +1,408 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.impl;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import io.opentracing.tag.Tags;
+import rocks.inspectit.agent.java.sdk.opentracing.Timer;
+import rocks.inspectit.shared.all.testbase.TestBase;
+
+/**
+ * @author Ivan Senic
+ *
+ */
+@SuppressWarnings("PMD")
+public class SpanImplTest extends TestBase {
+
+ @InjectMocks
+ SpanImpl span;
+
+ @Mock
+ Timer timer;
+
+ @Mock
+ TracerImpl tracer;
+
+ @BeforeMethod
+ public void init() {
+ when(tracer.getTimer()).thenReturn(timer);
+ }
+
+ public static class Start extends SpanImplTest {
+
+ @Test
+ public void happyPath() {
+ long startTime = 1442l;
+
+ span.start(startTime, 0);
+
+ assertThat(span.getStartTimeMicros(), is(startTime));
+ verify(tracer).spanStarted(span);
+ verifyNoMoreInteractions(tracer);
+ verifyZeroInteractions(timer);
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void startTimeNotProvided() {
+ span.start(0, 0);
+ }
+
+ }
+
+ public static class Finish extends SpanImplTest {
+
+ @Test
+ public void noNanos() {
+ long startTime = System.currentTimeMillis();
+ long endTime = startTime + 1223l;
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(endTime);
+ span.start(startTime, 0);
+
+ span.finish();
+
+ assertThat(span.getStartTimeMicros(), is(startTime));
+ assertThat(span.getDuration(), is((double) endTime - startTime));
+ verify(tracer).spanStarted(span);
+ verify(tracer).spanEnded(span);
+ verify(tracer, atLeastOnce()).getTimer();
+ verify(timer).getCurrentTimeMicroseconds();
+ verifyNoMoreInteractions(tracer, timer);
+ }
+
+ @Test
+ public void noNanosExplicit() {
+ long startTime = System.currentTimeMillis();
+ long endTime = startTime + 1223l;
+ span.start(startTime, 0);
+
+ span.finish(endTime);
+
+ assertThat(span.getStartTimeMicros(), is(startTime));
+ assertThat(span.getDuration(), is((double) endTime - startTime));
+ verify(tracer).spanStarted(span);
+ verify(tracer).spanEnded(span);
+ verifyNoMoreInteractions(tracer);
+ verifyZeroInteractions(timer);
+ }
+
+ @Test
+ public void nanos() {
+ long startTime = System.currentTimeMillis();
+ long startTimeNanos = System.nanoTime();
+ long endTimeNanos = startTimeNanos + 22546l;
+ when(timer.getCurrentNanoTime()).thenReturn(endTimeNanos);
+ span.start(startTime, startTimeNanos);
+
+ span.finish();
+
+ assertThat(span.getStartTimeMicros(), is(startTime));
+ assertThat(span.getDuration(), is((endTimeNanos - startTimeNanos) / 1000.d));
+ verify(tracer).spanStarted(span);
+ verify(tracer).spanEnded(span);
+ verify(tracer, atLeastOnce()).getTimer();
+ verify(timer).getCurrentNanoTime();
+ verifyNoMoreInteractions(tracer, timer);
+ }
+
+ @Test
+ public void nanosExplicit() {
+ long startTime = System.currentTimeMillis();
+ long endTime = startTime + 1223l;
+ span.start(startTime, 5467);
+
+ span.finish(endTime);
+
+ assertThat(span.getStartTimeMicros(), is(startTime));
+ assertThat(span.getDuration(), is((double) endTime - startTime));
+ verify(tracer).spanStarted(span);
+ verify(tracer).spanEnded(span);
+ verifyNoMoreInteractions(tracer);
+ verifyZeroInteractions(timer);
+ }
+
+ }
+
+ public static class Close extends SpanImplTest {
+
+ @Test
+ public void noNanos() {
+ long startTime = System.currentTimeMillis();
+ long endTime = startTime + 1223l;
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(endTime);
+ span.start(startTime, 0);
+
+ span.close();
+
+ assertThat(span.getStartTimeMicros(), is(startTime));
+ assertThat(span.getDuration(), is((double) endTime - startTime));
+ verify(tracer).spanStarted(span);
+ verify(tracer).spanEnded(span);
+ verify(tracer, atLeastOnce()).getTimer();
+ verify(timer).getCurrentTimeMicroseconds();
+ verifyNoMoreInteractions(tracer, timer);
+ }
+
+ @Test
+ public void nanos() {
+ long startTime = System.currentTimeMillis();
+ long startTimeNanos = System.nanoTime();
+ long endTimeNanos = startTimeNanos + 22546l;
+ when(timer.getCurrentNanoTime()).thenReturn(endTimeNanos);
+ span.start(startTime, startTimeNanos);
+
+ span.close();
+
+ assertThat(span.getStartTimeMicros(), is(startTime));
+ assertThat(span.getDuration(), is((endTimeNanos - startTimeNanos) / 1000.d));
+ verify(tracer).spanStarted(span);
+ verify(tracer).spanEnded(span);
+ verify(tracer, atLeastOnce()).getTimer();
+ verify(timer).getCurrentNanoTime();
+ verifyNoMoreInteractions(tracer, timer);
+ }
+ }
+
+ public static class SetTag extends SpanImplTest {
+
+ @Test
+ public void booleanTag() {
+ span.setTag("key", false);
+
+ assertThat(span.getTags().size(), is(1));
+ assertThat(span.getTags(), hasEntry("key", String.valueOf(false)));
+ }
+
+ @Test
+ public void numberTag() {
+ span.setTag("key", 5l);
+
+ assertThat(span.getTags().size(), is(1));
+ assertThat(span.getTags(), hasEntry("key", String.valueOf(5L)));
+ }
+
+ @Test
+ public void stringTag() {
+ span.setTag("key", "value");
+
+ assertThat(span.getTags().size(), is(1));
+ assertThat(span.getTags(), hasEntry("key", "value"));
+ }
+ }
+
+ public static class Context extends SpanImplTest {
+
+ @Test
+ public void setContext() {
+ SpanContextImpl spanContext = SpanContextImpl.build();
+ span.setSpanContext(spanContext);
+
+ SpanContextImpl context = span.context();
+
+ assertThat(context, is(spanContext));
+ }
+ }
+
+ public static class SetBaggageItem extends SpanImplTest {
+
+ @Test
+ public void happyPath() {
+ SpanContextImpl spanContext = SpanContextImpl.build();
+ span.setSpanContext(spanContext);
+
+ span.setBaggageItem("key", "value");
+
+ assertThat(span.context().getBaggageItem("key"), is("value"));
+ }
+
+ @Test
+ public void contextNull() {
+ span.setBaggageItem("key", "value");
+ }
+ }
+
+ public static class GetBaggageItem extends SpanImplTest {
+
+ @Test
+ public void contextNull() {
+ String baggageItem = span.getBaggageItem("key");
+
+ assertThat(baggageItem, is(nullValue()));
+ }
+ }
+
+ public static class IsClient extends SpanImplTest {
+
+ @Test
+ public void specified() {
+ Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT);
+
+ boolean client = span.isClient();
+
+ assertThat(client, is(true));
+ }
+
+ @Test
+ public void notSpecified() {
+ boolean client = span.isClient();
+
+ assertThat(client, is(false));
+ }
+
+ @Test
+ public void notClient() {
+ Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_SERVER);
+
+ boolean client = span.isClient();
+
+ assertThat(client, is(false));
+ }
+ }
+
+ public static class IsServer extends SpanImplTest {
+
+ @Test
+ public void specified() {
+ Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_SERVER);
+
+ boolean server = span.isServer();
+
+ assertThat(server, is(true));
+ }
+
+ @Test
+ public void notSpecified() {
+ boolean server = span.isServer();
+
+ assertThat(server, is(true));
+ }
+
+ @Test
+ public void notClient() {
+ Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT);
+
+ boolean server = span.isServer();
+
+ assertThat(server, is(false));
+ }
+ }
+
+ public static class Log extends SpanImplTest {
+
+ @Test
+ public void onlyPayload() {
+ long time = 3444l;
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(time);
+ String payload = "sent";
+
+ span.log(payload);
+
+ // not impemneted, ignored
+ verifyNoMoreInteractions(timer, tracer);
+ }
+
+ @Test
+ public void payloadNull() {
+ long time = 3444l;
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(time);
+
+ span.log((String) null);
+
+ // not impemneted, ignored
+ verifyNoMoreInteractions(timer, tracer);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void nameAndPayload() {
+ long time = 3444l;
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(time);
+ String payload = "sent";
+ String eventName = "myEventName";
+
+ span.log(eventName, payload);
+
+ // not impemneted, ignored
+ verifyNoMoreInteractions(timer, tracer);
+ }
+
+ @Test
+ public void asMap() {
+ long time = 3444l;
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(time);
+ Map map = new HashMap();
+ map.put("e1", "p1");
+ map.put("e2", false);
+ map.put("e3", 12d);
+
+ span.log(map);
+
+ // not impemneted, ignored
+ verifyNoMoreInteractions(timer, tracer);
+ }
+
+ @Test
+ public void asMapNull() {
+ Map m = null;
+
+ span.log(1L, m);
+
+ // not impemneted, ignored
+ verifyNoMoreInteractions(timer, tracer);
+ }
+
+ @Test
+ public void onlyPayloadWithTime() {
+ long time = 3444l;
+ String payload = "sent";
+
+ span.log(time, payload);
+
+ // not impemneted, ignored
+ verifyNoMoreInteractions(timer, tracer);
+
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void nameAndPayloadWithTime() {
+ long time = 3444l;
+ String payload = "sent";
+ String eventName = "myEventName";
+
+ span.log(time, eventName, payload);
+
+ // not impemneted, ignored
+ verifyNoMoreInteractions(timer, tracer);
+ }
+
+ @Test
+ public void asMapWihtTime() {
+ long time = 3444l;
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(time);
+ Map map = new HashMap();
+ map.put("e1", "p1");
+ map.put("e2", false);
+ map.put("e3", 12d);
+
+ span.log(time, map);
+
+ // not impemneted, ignored
+ verifyNoMoreInteractions(timer, tracer);
+ }
+ }
+}
diff --git a/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/TracerImplTest.java b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/TracerImplTest.java
new file mode 100644
index 000000000..f2987369b
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/impl/TracerImplTest.java
@@ -0,0 +1,399 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.impl;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import org.mockito.Mock;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import io.opentracing.References;
+import io.opentracing.SpanContext;
+import io.opentracing.propagation.Format;
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.sdk.opentracing.ExtendedTracer;
+import rocks.inspectit.agent.java.sdk.opentracing.Reporter;
+import rocks.inspectit.agent.java.sdk.opentracing.Timer;
+import rocks.inspectit.agent.java.sdk.opentracing.TracerProvider;
+import rocks.inspectit.agent.java.sdk.opentracing.propagation.Propagator;
+import rocks.inspectit.shared.all.testbase.TestBase;
+
+/**
+ * @author Ivan Senic
+ *
+ */
+@SuppressWarnings("PMD")
+public class TracerImplTest extends TestBase {
+
+ TracerImpl tracer;
+
+ @Mock
+ Timer timer;
+
+ @Mock
+ Reporter reporter;
+
+ @BeforeMethod
+ public void init() {
+ tracer = new TracerImpl(timer, reporter, false);
+ }
+
+ public static class Constructor extends TracerImplTest {
+
+ @Test
+ public void noArg() {
+ tracer = new TracerImpl();
+
+ assertThat(tracer.getTimer(), is(not(nullValue())));
+ assertThat(TracerProvider.get(false), is(nullValue()));
+ }
+
+ @Test
+ public void setToProvider() {
+ tracer = new TracerImpl(timer, reporter, true);
+
+ assertThat(tracer.getTimer(), is(not(nullValue())));
+ assertThat(TracerProvider.get(), is((ExtendedTracer) tracer));
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void nullTimer() {
+ new TracerImpl(null, reporter, false);
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void nullReporter() {
+ new TracerImpl(timer, null, false);
+ }
+
+ }
+
+ public static class BuildSpan extends TracerImplTest {
+
+ @Test
+ public void base() {
+ long time = 122254L;
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(time);
+
+ SpanBuilderImpl builder = tracer.buildSpan();
+
+ SpanImpl span = builder.start();
+ assertThat(span.getOperationName(), is(nullValue()));
+ assertThat(span.getStartTimeMicros(), is(time));
+ assertThat(tracer.getCurrentContext(), is(span.context()));
+ assertThat(tracer.isCurrentContextExisting(), is(true));
+ }
+
+ @Test
+ public void withOperationName() {
+ long time = 122254L;
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(time);
+ String operationName = "op";
+
+ SpanBuilderImpl builder = tracer.buildSpan(operationName);
+
+ SpanImpl span = builder.start();
+ assertThat(span.getOperationName(), is(operationName));
+ assertThat(span.getStartTimeMicros(), is(time));
+ assertThat(tracer.getCurrentContext(), is(span.context()));
+ assertThat(tracer.isCurrentContextExisting(), is(true));
+ }
+
+ @Test
+ public void withThreadContext() {
+ long time = 122254L;
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(time);
+ String operationName = "op";
+ String referenceType = References.FOLLOWS_FROM;
+ SpanImpl first = tracer.buildSpan().start();
+
+ SpanBuilderImpl builder = tracer.buildSpan(operationName, referenceType, true);
+
+ SpanImpl span = builder.start();
+ assertThat(span.getOperationName(), is(operationName));
+ assertThat(span.getStartTimeMicros(), is(time));
+ assertThat(span.context().getReferenceType(), is(referenceType));
+ assertThat(span.context().getTraceId(), is(first.context().getTraceId()));
+ assertThat(span.context().getParentId(), is(first.context().getId()));
+ assertThat(tracer.getCurrentContext(), is(span.context()));
+ assertThat(tracer.isCurrentContextExisting(), is(true));
+ }
+
+ @Test
+ public void withoutThreadContext() {
+ long time = 122254L;
+ when(timer.getCurrentTimeMicroseconds()).thenReturn(time);
+ String operationName = "op";
+ String referenceType = References.FOLLOWS_FROM;
+ tracer.buildSpan().start();
+
+ SpanBuilderImpl builder = tracer.buildSpan(operationName, referenceType, false);
+
+ SpanImpl span = builder.start();
+ assertThat(span.getOperationName(), is(operationName));
+ assertThat(span.getStartTimeMicros(), is(time));
+ assertThat(span.context().getReferenceType(), is(nullValue()));
+ assertThat(span.context().getParentId(), is(span.context().getId()));
+ assertThat(tracer.getCurrentContext(), is(span.context()));
+ assertThat(tracer.isCurrentContextExisting(), is(true));
+ }
+
+ }
+
+ public static class Inject extends TracerImplTest {
+
+ @Mock
+ Propagator propagator;
+
+ @Mock
+ SpanContextImpl context;
+
+ @Mock
+ TextMap carrier;
+
+ @Test
+ public void happyPath() {
+ tracer.registerPropagator(Format.Builtin.TEXT_MAP, propagator);
+
+ tracer.inject(context, Format.Builtin.TEXT_MAP, carrier);
+
+ verify(propagator).inject(context, carrier);
+ verifyNoMoreInteractions(propagator);
+ }
+
+ @Test
+ public void carrierNull() {
+ tracer.registerPropagator(Format.Builtin.TEXT_MAP, propagator);
+
+ tracer.inject(context, Format.Builtin.TEXT_MAP, null);
+
+ verifyZeroInteractions(propagator);
+ }
+
+ @Test
+ public void formatNull() {
+ tracer.registerPropagator(Format.Builtin.TEXT_MAP, propagator);
+
+ tracer.inject(context, null, carrier);
+
+ verifyZeroInteractions(propagator);
+ }
+
+ @Test
+ public void wrongContext() {
+ tracer.registerPropagator(Format.Builtin.TEXT_MAP, propagator);
+
+ tracer.inject(mock(SpanContext.class), Format.Builtin.TEXT_MAP, carrier);
+
+ verifyZeroInteractions(propagator);
+ }
+
+ @Test
+ public void nonExistingPropagator() {
+ Format format = new Format() {
+ };
+ String string = "test";
+
+ tracer.inject(context, format, string);
+ }
+
+ }
+
+ public static class Extract extends TracerImplTest {
+
+ @Mock
+ Propagator propagator;
+
+ @Mock
+ SpanContextImpl context;
+
+ @Mock
+ TextMap carrier;
+
+ @Test
+ public void happyPath() {
+ when(propagator.extract(carrier)).thenReturn(context);
+ tracer.registerPropagator(Format.Builtin.TEXT_MAP, propagator);
+
+ SpanContext extracted = tracer.extract(Format.Builtin.TEXT_MAP, carrier);
+
+ assertThat(extracted, is((SpanContext) context));
+ verify(propagator).extract(carrier);
+ verifyNoMoreInteractions(propagator);
+ }
+
+ @Test
+ public void carrierNull() {
+ tracer.registerPropagator(Format.Builtin.TEXT_MAP, propagator);
+
+ SpanContext extracted = tracer.extract(Format.Builtin.TEXT_MAP, null);
+
+ assertThat(extracted, is(nullValue()));
+ verifyZeroInteractions(propagator);
+ }
+
+ @Test
+ public void formatNull() {
+ tracer.registerPropagator(Format.Builtin.TEXT_MAP, propagator);
+
+ SpanContext extracted = tracer.extract(null, carrier);
+
+ assertThat(extracted, is(nullValue()));
+ verifyZeroInteractions(propagator);
+ }
+
+ @Test
+ public void nonExistingPropagator() {
+ Format format = new Format() {
+ };
+ String string = "test";
+
+ SpanContext extracted = tracer.extract(format, string);
+
+ assertThat(extracted, is(nullValue()));
+ }
+
+ }
+
+ public static class SpanStarted extends TracerImplTest {
+
+ @Test
+ public void happyPath() {
+ SpanImpl span = new SpanImpl(tracer);
+ span.setSpanContext(SpanContextImpl.build());
+
+ tracer.spanStarted(span);
+
+ assertThat(tracer.getCurrentContext(), is(span.context()));
+ assertThat(tracer.isCurrentContextExisting(), is(true));
+ }
+
+ @Test
+ public void spanNull() {
+ tracer.spanStarted(null);
+
+ assertThat(tracer.getCurrentContext(), is(nullValue()));
+ assertThat(tracer.isCurrentContextExisting(), is(false));
+ }
+ }
+
+ public static class SpanEnded extends TracerImplTest {
+
+ @Test
+ public void happyPath() {
+ SpanImpl span = new SpanImpl(tracer);
+ span.setSpanContext(SpanContextImpl.build());
+ tracer.spanStarted(span);
+
+ tracer.spanEnded(span);
+
+ assertThat(tracer.getCurrentContext(), is(nullValue()));
+ assertThat(tracer.isCurrentContextExisting(), is(false));
+ verify(reporter).report(span);
+ verifyNoMoreInteractions(reporter);
+ }
+
+ @Test
+ public void spanNull() {
+ SpanImpl span = new SpanImpl(tracer);
+ span.setSpanContext(SpanContextImpl.build());
+ tracer.spanStarted(span);
+
+ tracer.spanEnded(null);
+
+ assertThat(tracer.getCurrentContext(), is(span.context()));
+ assertThat(tracer.isCurrentContextExisting(), is(true));
+ verifyZeroInteractions(reporter);
+ }
+
+ @Test
+ public void noReporting() {
+ SpanImpl span = new SpanImpl(tracer);
+ span.setSpanContext(SpanContextImpl.build());
+ span.setReport(false);
+ tracer.spanStarted(span);
+
+ tracer.spanEnded(span);
+
+ assertThat(tracer.getCurrentContext(), is(nullValue()));
+ assertThat(tracer.isCurrentContextExisting(), is(false));
+ verifyZeroInteractions(reporter);
+ }
+
+ @Test
+ public void twoSpans() {
+ SpanImpl span1 = new SpanImpl(tracer);
+ span1.setSpanContext(SpanContextImpl.build());
+ SpanImpl span2 = new SpanImpl(tracer);
+ span2.setSpanContext(SpanContextImpl.build());
+ tracer.spanStarted(span1);
+ tracer.spanStarted(span2);
+
+ tracer.spanEnded(span2);
+
+ assertThat(tracer.getCurrentContext(), is(span1.context()));
+ assertThat(tracer.isCurrentContextExisting(), is(true));
+ verify(reporter).report(span2);
+ verifyNoMoreInteractions(reporter);
+ }
+
+ @Test
+ public void twoSpansFirstEndsBeforeSecond() {
+ SpanImpl span1 = new SpanImpl(tracer);
+ span1.setSpanContext(SpanContextImpl.build());
+ SpanImpl span2 = new SpanImpl(tracer);
+ span2.setSpanContext(SpanContextImpl.build());
+ tracer.spanStarted(span1);
+ tracer.spanStarted(span2);
+
+ tracer.spanEnded(span1);
+
+ assertThat(tracer.getCurrentContext(), is(nullValue()));
+ assertThat(tracer.isCurrentContextExisting(), is(false));
+ verify(reporter).report(span1);
+ verifyNoMoreInteractions(reporter);
+ }
+
+ @Test
+ public void twoSpansOneNotStarted() {
+ SpanImpl span1 = new SpanImpl(tracer);
+ span1.setSpanContext(SpanContextImpl.build());
+ SpanImpl span2 = new SpanImpl(tracer);
+ span2.setSpanContext(SpanContextImpl.build());
+ tracer.spanStarted(span1);
+
+ tracer.spanEnded(span2);
+
+ assertThat(tracer.getCurrentContext(), is(span1.context()));
+ assertThat(tracer.isCurrentContextExisting(), is(true));
+ verify(reporter).report(span2);
+ verifyNoMoreInteractions(reporter);
+ }
+ }
+
+ public static class SetTimer extends TracerImplTest {
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void timerNull() {
+ tracer.setTimer(null);
+ }
+
+ @Test
+ public void set() {
+ Timer t = mock(Timer.class);
+
+ tracer.setTimer(t);
+
+ assertThat(tracer.getTimer(), is(t));
+ }
+
+ }
+
+}
diff --git a/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/propagation/TextMapPropagatorTest.java b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/propagation/TextMapPropagatorTest.java
new file mode 100644
index 000000000..ef3e75f8a
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/propagation/TextMapPropagatorTest.java
@@ -0,0 +1,160 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.propagation;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.testng.annotations.Test;
+
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.constants.PropagationConstants;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanContextImpl;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.util.ConversionUtils;
+import rocks.inspectit.shared.all.testbase.TestBase;
+
+/**
+ * @author Ivan Senic
+ *
+ */
+@SuppressWarnings("PMD")
+public class TextMapPropagatorTest extends TestBase {
+
+ @InjectMocks
+ TextMapPropagator propagator;
+
+ @Mock
+ TextMap carrier;
+
+ public static class Inject extends TextMapPropagatorTest {
+
+ @Test
+ public void ids() {
+ SpanContextImpl context = SpanContextImpl.build();
+
+ propagator.inject(context, carrier);
+
+ verify(carrier).put(PropagationConstants.SPAN_ID, ConversionUtils.toHexString(context.getId()));
+ verify(carrier).put(PropagationConstants.TRACE_ID, ConversionUtils.toHexString(context.getTraceId()));
+ verifyNoMoreInteractions(carrier);
+ }
+
+ @Test
+ public void baggage() {
+ SpanContextImpl context = SpanContextImpl.build();
+ context.setBaggageItem("key", "value");
+
+ propagator.inject(context, carrier);
+
+ verify(carrier).put(PropagationConstants.INSPECTIT_BAGGAGE_PREFIX + "key", "value");
+ }
+
+ @Test
+ public void nullContext() {
+ propagator.inject(null, carrier);
+
+ verifyZeroInteractions(carrier);
+ }
+ }
+
+ public static class Extract extends TextMapPropagatorTest {
+
+ @Test
+ public void ids() {
+ Map map = new HashMap();
+ map.put(PropagationConstants.SPAN_ID, ConversionUtils.toHexString(1L));
+ map.put(PropagationConstants.TRACE_ID, ConversionUtils.toHexString(2L));
+ when(carrier.iterator()).thenReturn(map.entrySet().iterator());
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context.getId(), is(1L));
+ assertThat(context.getTraceId(), is(2L));
+ assertThat(context.baggageItems().iterator().hasNext(), is(false));
+ }
+
+ @Test
+ public void idsWrong() {
+ Map map = new HashMap();
+ map.put(PropagationConstants.SPAN_ID, "whatever");
+ map.put(PropagationConstants.TRACE_ID, "whatever");
+ when(carrier.iterator()).thenReturn(map.entrySet().iterator());
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context, is(nullValue()));
+ }
+
+ @Test
+ public void onlySpanid() {
+ Map map = new HashMap();
+ map.put(PropagationConstants.SPAN_ID, ConversionUtils.toHexString(1L));
+ when(carrier.iterator()).thenReturn(map.entrySet().iterator());
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context, is(nullValue()));
+ }
+
+ @Test
+ public void onlyTraceId() {
+ Map map = new HashMap();
+ map.put(PropagationConstants.TRACE_ID, ConversionUtils.toHexString(2L));
+ when(carrier.iterator()).thenReturn(map.entrySet().iterator());
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context, is(nullValue()));
+ }
+
+ @Test
+ public void idsAndBaggage() {
+ Map map = new HashMap();
+ map.put(PropagationConstants.SPAN_ID, ConversionUtils.toHexString(1L));
+ map.put(PropagationConstants.TRACE_ID, ConversionUtils.toHexString(2L));
+ map.put(PropagationConstants.INSPECTIT_BAGGAGE_PREFIX + "key", "value");
+ when(carrier.iterator()).thenReturn(map.entrySet().iterator());
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context.getId(), is(1L));
+ assertThat(context.getTraceId(), is(2L));
+ Entry entry = context.baggageItems().iterator().next();
+ assertThat(entry.getKey(), is("key"));
+ assertThat(entry.getValue(), is("value"));
+ }
+
+ @Test
+ public void idsAndNotOurBaggage() {
+ Map map = new HashMap();
+ map.put(PropagationConstants.SPAN_ID, ConversionUtils.toHexString(1L));
+ map.put(PropagationConstants.TRACE_ID, ConversionUtils.toHexString(2L));
+ map.put("somekey", "value");
+ when(carrier.iterator()).thenReturn(map.entrySet().iterator());
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context.getId(), is(1L));
+ assertThat(context.getTraceId(), is(2L));
+ assertThat(context.baggageItems().iterator().hasNext(), is(false));
+ }
+
+ @Test
+ public void carrierProvidesNullIterator() {
+ when(carrier.iterator()).thenReturn(null);
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context, is(nullValue()));
+ }
+ }
+}
diff --git a/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/propagation/UrlEncodingPropagatorTest.java b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/propagation/UrlEncodingPropagatorTest.java
new file mode 100644
index 000000000..3c6a96e87
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/propagation/UrlEncodingPropagatorTest.java
@@ -0,0 +1,161 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.propagation;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.testng.annotations.Test;
+
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.constants.PropagationConstants;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanContextImpl;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.util.ConversionUtils;
+import rocks.inspectit.shared.all.testbase.TestBase;
+
+/**
+ * @author Ivan Senic
+ *
+ */
+@SuppressWarnings("PMD")
+public class UrlEncodingPropagatorTest extends TestBase {
+
+ @InjectMocks
+ UrlEncodingPropagator propagator;
+
+ @Mock
+ TextMap carrier;
+
+ public static class Inject extends UrlEncodingPropagatorTest {
+
+ @Test
+ public void ids() {
+ SpanContextImpl context = SpanContextImpl.build();
+
+ propagator.inject(context, carrier);
+
+ verify(carrier).put(PropagationConstants.SPAN_ID, ConversionUtils.toHexString(context.getId()));
+ verify(carrier).put(PropagationConstants.TRACE_ID, ConversionUtils.toHexString(context.getTraceId()));
+ verifyNoMoreInteractions(carrier);
+ }
+
+ @Test
+ public void baggage() {
+ SpanContextImpl context = SpanContextImpl.build();
+ context.setBaggageItem("key", "value with spaces");
+
+ propagator.inject(context, carrier);
+
+ verify(carrier).put(PropagationConstants.INSPECTIT_BAGGAGE_PREFIX + "key", "value+with+spaces");
+ }
+
+ @Test
+ public void nullContext() {
+ propagator.inject(null, carrier);
+
+ verifyZeroInteractions(carrier);
+ }
+
+ }
+
+ public static class Extract extends UrlEncodingPropagatorTest {
+
+ @Test
+ public void ids() {
+ Map map = new HashMap();
+ map.put(PropagationConstants.SPAN_ID, ConversionUtils.toHexString(1L));
+ map.put(PropagationConstants.TRACE_ID, ConversionUtils.toHexString(2L));
+ when(carrier.iterator()).thenReturn(map.entrySet().iterator());
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context.getId(), is(1L));
+ assertThat(context.getTraceId(), is(2L));
+ assertThat(context.baggageItems().iterator().hasNext(), is(false));
+ }
+
+ @Test
+ public void idsWrong() {
+ Map map = new HashMap();
+ map.put(PropagationConstants.SPAN_ID, "whatever");
+ map.put(PropagationConstants.TRACE_ID, "whatever");
+ when(carrier.iterator()).thenReturn(map.entrySet().iterator());
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context, is(nullValue()));
+ }
+
+ @Test
+ public void onlySpanid() {
+ Map map = new HashMap();
+ map.put(PropagationConstants.SPAN_ID, ConversionUtils.toHexString(1L));
+ when(carrier.iterator()).thenReturn(map.entrySet().iterator());
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context, is(nullValue()));
+ }
+
+ @Test
+ public void onlyTraceId() {
+ Map map = new HashMap();
+ map.put(PropagationConstants.TRACE_ID, ConversionUtils.toHexString(2L));
+ when(carrier.iterator()).thenReturn(map.entrySet().iterator());
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context, is(nullValue()));
+ }
+
+ @Test
+ public void idsAndBaggage() {
+ Map map = new HashMap();
+ map.put(PropagationConstants.SPAN_ID, ConversionUtils.toHexString(1L));
+ map.put(PropagationConstants.TRACE_ID, ConversionUtils.toHexString(2L));
+ map.put(PropagationConstants.INSPECTIT_BAGGAGE_PREFIX + "key", "value+with+spaces");
+ when(carrier.iterator()).thenReturn(map.entrySet().iterator());
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context.getId(), is(1L));
+ assertThat(context.getTraceId(), is(2L));
+ Entry entry = context.baggageItems().iterator().next();
+ assertThat(entry.getKey(), is("key"));
+ assertThat(entry.getValue(), is("value with spaces"));
+ }
+
+ @Test
+ public void idsAndNotOurBaggage() {
+ Map map = new HashMap();
+ map.put(PropagationConstants.SPAN_ID, ConversionUtils.toHexString(1L));
+ map.put(PropagationConstants.TRACE_ID, ConversionUtils.toHexString(2L));
+ map.put("somekey", "value");
+ when(carrier.iterator()).thenReturn(map.entrySet().iterator());
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context.getId(), is(1L));
+ assertThat(context.getTraceId(), is(2L));
+ assertThat(context.baggageItems().iterator().hasNext(), is(false));
+ }
+
+ @Test
+ public void carrierProvidesNullIterator() {
+ when(carrier.iterator()).thenReturn(null);
+
+ SpanContextImpl context = propagator.extract(carrier);
+
+ assertThat(context, is(nullValue()));
+ }
+ }
+}
diff --git a/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/util/ConversionUtilsTest.java b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/util/ConversionUtilsTest.java
new file mode 100644
index 000000000..62f62679a
--- /dev/null
+++ b/inspectit.agent.java.sdk/src/test/java/rocks/inspectit/agent/java/sdk/opentracing/internal/util/ConversionUtilsTest.java
@@ -0,0 +1,40 @@
+package rocks.inspectit.agent.java.sdk.opentracing.internal.util;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import org.testng.annotations.Test;
+
+/**
+ * @author Ivan Senic
+ *
+ */
+@SuppressWarnings("PMD")
+public class ConversionUtilsTest {
+
+ public static class ParseHexStringSafe {
+
+ @Test
+ public void nullString() {
+ long parsed = ConversionUtils.parseHexStringSafe(null);
+
+ assertThat(parsed, is(0L));
+ }
+
+ @Test(expectedExceptions = NumberFormatException.class)
+ public void notParsable() {
+ ConversionUtils.parseHexStringSafe("something funny");
+ }
+
+ @Test(invocationCount = 10)
+ public void random() {
+ long generated = RandomUtils.randomLong();
+ String str = ConversionUtils.toHexString(generated);
+
+ long parsed = ConversionUtils.parseHexStringSafe(str);
+
+ assertThat(parsed, is(generated));
+ }
+
+ }
+}
diff --git a/inspectit.agent.java/inspectit.agent.java.gradle b/inspectit.agent.java/inspectit.agent.java.gradle
index 76b341fce..08afead4a 100644
--- a/inspectit.agent.java/inspectit.agent.java.gradle
+++ b/inspectit.agent.java/inspectit.agent.java.gradle
@@ -40,6 +40,7 @@ configurations {
/** Depend on inspectit.shared.all, testCompile must depend on shared all test sources because of TestBase class */
dependencies {
compile project(':inspectit.shared.all')
+ compile project(':inspectit.agent.java.sdk')
testCompile project (path: ':inspectit.shared.all', configuration: 'testArchives')
}
@@ -74,6 +75,7 @@ jar {
into('libs') {
from project(':inspectit.shared.all').jar.outputs
+ from project(':inspectit.agent.java.sdk').jar.outputs
from configurations.agentJavaProd
}
diff --git a/inspectit.agent.java/src/main/external-resources/logging-config.xml b/inspectit.agent.java/src/main/external-resources/logging-config.xml
index 741d59b69..79177cbe9 100644
--- a/inspectit.agent.java/src/main/external-resources/logging-config.xml
+++ b/inspectit.agent.java/src/main/external-resources/logging-config.xml
@@ -82,6 +82,10 @@
+
+
+
+
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/SpringAgent.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/SpringAgent.java
index edeecc046..a671c4e83 100644
--- a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/SpringAgent.java
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/SpringAgent.java
@@ -17,7 +17,10 @@
import rocks.inspectit.agent.java.hooking.IHookDispatcher;
import rocks.inspectit.agent.java.instrumentation.IInstrumentationAware;
import rocks.inspectit.agent.java.logback.LogInitializer;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.TracerLoggerProvider;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.TracerLoggerWrapper;
import rocks.inspectit.agent.java.spring.SpringConfiguration;
+import rocks.inspectit.agent.java.tracing.core.Slf4jTracerLoggerProvider;
import rocks.inspectit.shared.all.instrumentation.config.impl.RetransformationStrategy;
import rocks.inspectit.shared.all.pattern.IMatchPattern;
import rocks.inspectit.shared.all.util.UnderlyingSystemInfo;
@@ -144,6 +147,11 @@ public void run() {
};
});
+ // IMPORTANT: set the logger provider before starting the spring
+ // this was we ensure we use correct provider before any SDK classes are loaded
+ TracerLoggerProvider provider = new Slf4jTracerLoggerProvider();
+ TracerLoggerWrapper.setProvider(provider);
+
// set inspectIT class loader to be the context class loader
// so that bean factory can use correct class loader for finding the classes
ClassLoader inspectITClassLoader = this.getClass().getClassLoader();
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/javaagent/JavaAgent.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/javaagent/JavaAgent.java
index 86cf06ee3..90c004acb 100644
--- a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/javaagent/JavaAgent.java
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/javaagent/JavaAgent.java
@@ -91,9 +91,14 @@ public static void premain(String agentArgs, Instrumentation inst) {
// Starting up the real agent
try {
- // initialize the SpringAgent
+ // append all needed jars to the boot class loader
@SuppressWarnings("resource")
InspectItClassLoader classLoader = new InspectItClassLoader(new URL[0]);
+ for (String jarPath : classLoader.getBootClassLoaderJarFiles()) {
+ inst.appendToBootstrapClassLoaderSearch(new JarFile(jarPath));
+ }
+
+ // initialize the SpringAgent
Class> agentClazz = classLoader.loadClass(INSPECTIT_AGENT);
Constructor> constructor = agentClazz.getConstructor(File.class, Instrumentation.class);
Object realAgent = constructor.newInstance(getInspectItAgentJarFileLocation(), inst);
@@ -319,6 +324,11 @@ public static class InspectItClassLoader extends URLClassLoader {
*/
private final Set ignoreClasses = new HashSet();
+ /**
+ * Set of jar files that need to be appended to the boot class loader.
+ */
+ private Set bootClassLoaderJarFiles = new HashSet();
+
/**
* Default constructor initialized with the urls of the dependency jars etc.
*
@@ -355,6 +365,15 @@ public InspectItClassLoader(URL[] urls) {
ignoreClasses.add(InspectItClassLoader.class.getName());
}
+ /**
+ * Gets {@link #bootClassLoaderJarFiles}.
+ *
+ * @return {@link #bootClassLoaderJarFiles}
+ */
+ public Set getBootClassLoaderJarFiles() {
+ return this.bootClassLoaderJarFiles;
+ }
+
/**
* Analyze this jar file for containing jar files and classes to be used in our own
* classloader.
@@ -373,7 +392,11 @@ private void addJarResource(File file) throws IOException {
JarEntry jarEntry = jarEntries.nextElement();
if (!jarEntry.isDirectory() && isJar(jarEntry.getName())) {
File jar = jarEntryAsFile(jarFile, jarEntry);
- addJarResource(jar);
+ if (jar.getName().contains("io.opentracing")) {
+ bootClassLoaderJarFiles.add(jar.getAbsolutePath());
+ } else {
+ addJarResource(jar);
+ }
}
}
}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/invocationsequence/InvocationSequenceHook.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/invocationsequence/InvocationSequenceHook.java
index fac44e88c..f574713b0 100644
--- a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/invocationsequence/InvocationSequenceHook.java
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/invocationsequence/InvocationSequenceHook.java
@@ -19,6 +19,7 @@
import rocks.inspectit.agent.java.core.ListListener;
import rocks.inspectit.agent.java.hooking.IConstructorHook;
import rocks.inspectit.agent.java.hooking.IMethodHook;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.TracerImpl;
import rocks.inspectit.agent.java.sending.ISendingStrategy;
import rocks.inspectit.agent.java.sensor.exception.ExceptionSensor;
import rocks.inspectit.agent.java.sensor.method.IMethodSensor;
@@ -26,6 +27,7 @@
import rocks.inspectit.agent.java.sensor.method.jdbc.PreparedStatementParameterSensor;
import rocks.inspectit.agent.java.sensor.method.jdbc.PreparedStatementSensor;
import rocks.inspectit.agent.java.sensor.method.logging.Log4JLoggingSensor;
+import rocks.inspectit.agent.java.tracing.core.transformer.SpanContextTransformer;
import rocks.inspectit.agent.java.util.StringConstraint;
import rocks.inspectit.agent.java.util.ThreadLocalStack;
import rocks.inspectit.agent.java.util.Timer;
@@ -43,6 +45,7 @@
import rocks.inspectit.shared.all.communication.data.TimerData;
import rocks.inspectit.shared.all.instrumentation.config.impl.MethodSensorTypeConfig;
import rocks.inspectit.shared.all.instrumentation.config.impl.PlatformSensorTypeConfig;
+import rocks.inspectit.shared.all.tracing.data.AbstractSpan;
/**
* The invocation sequence hook stores the record of the invocation sequences in a
@@ -68,6 +71,16 @@ public class InvocationSequenceHook implements IMethodHook, IConstructorHook, IC
*/
private final IPlatformManager platformManager;
+ /**
+ * The real core service needed to delegate spans to.
+ */
+ private final ICoreService realCoreService;
+
+ /**
+ * Current tracer for the spans.
+ */
+ private final TracerImpl tracer;
+
/**
* The property accessor.
*/
@@ -124,6 +137,10 @@ public class InvocationSequenceHook implements IMethodHook, IConstructorHook, IC
* The timer.
* @param platformManager
* The Platform manager.
+ * @param coreService
+ * The real core service needed to delegate spans to.
+ * @param tracer
+ * Current tracer for the spans.
* @param propertyAccessor
* The property accessor.
* @param param
@@ -131,9 +148,12 @@ public class InvocationSequenceHook implements IMethodHook, IConstructorHook, IC
* @param enhancedExceptionSensor
* If enhanced exception sensor is ON.
*/
- public InvocationSequenceHook(Timer timer, IPlatformManager platformManager, IPropertyAccessor propertyAccessor, Map param, boolean enhancedExceptionSensor) {
+ public InvocationSequenceHook(Timer timer, IPlatformManager platformManager, ICoreService coreService, TracerImpl tracer, IPropertyAccessor propertyAccessor, Map param,
+ boolean enhancedExceptionSensor) {
this.timer = timer;
this.platformManager = platformManager;
+ this.realCoreService = coreService;
+ this.tracer = tracer;
this.propertyAccessor = propertyAccessor;
this.strConstraint = new StringConstraint(param);
this.enhancedExceptionSensor = enhancedExceptionSensor;
@@ -234,6 +254,11 @@ public void secondAfterBody(ICoreService coreService, long methodId, long sensor
double startTime = timeStack.pop().doubleValue();
double duration = endTime - startTime;
+ // check if we belong to a span
+ if (tracer.isCurrentContextExisting()) {
+ invocationSequenceData.setSpanIdent(SpanContextTransformer.transformSpanContext(tracer.getCurrentContext()));
+ }
+
// complete the sequence and store the data object in the 'true'
// core service so that it can be transmitted to the server. we
// just need an arbitrary prefix so that this sequence will
@@ -494,6 +519,11 @@ private void saveDataObject(DefaultData dataObject) {
LoggingData loggingData = (LoggingData) dataObject;
invocationSequenceData.setLoggingData(loggingData);
}
+
+ if (AbstractSpan.class.isAssignableFrom(dataObject.getClass())) {
+ AbstractSpan span = (AbstractSpan) dataObject;
+ invocationSequenceData.setSpanIdent(span.getSpanIdent());
+ }
}
// //////////////////////////////////////////////
@@ -510,6 +540,11 @@ public void addMethodSensorData(long sensorTypeId, long methodId, String prefix,
return;
}
saveDataObject(methodSensorData.finalizeData());
+
+ // delegate to real core service in case of the span
+ if (AbstractSpan.class.isAssignableFrom(methodSensorData.getClass())) {
+ realCoreService.addMethodSensorData(sensorTypeId, methodId, prefix, methodSensorData);
+ }
}
/**
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/invocationsequence/InvocationSequenceSensor.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/invocationsequence/InvocationSequenceSensor.java
index 1ed62de8a..ddf0e2f94 100644
--- a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/invocationsequence/InvocationSequenceSensor.java
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/invocationsequence/InvocationSequenceSensor.java
@@ -7,8 +7,10 @@
import rocks.inspectit.agent.java.config.IConfigurationStorage;
import rocks.inspectit.agent.java.config.IPropertyAccessor;
import rocks.inspectit.agent.java.config.StorageException;
+import rocks.inspectit.agent.java.core.ICoreService;
import rocks.inspectit.agent.java.core.IPlatformManager;
import rocks.inspectit.agent.java.hooking.IHook;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.TracerImpl;
import rocks.inspectit.agent.java.sensor.method.AbstractMethodSensor;
import rocks.inspectit.agent.java.sensor.method.IMethodSensor;
import rocks.inspectit.agent.java.util.Timer;
@@ -35,46 +37,33 @@ public class InvocationSequenceSensor extends AbstractMethodSensor implements IM
private IPlatformManager platformManager;
/**
- * The property accessor.
+ * {@link ICoreService}.
*/
@Autowired
- private IPropertyAccessor propertyAccessor;
+ private ICoreService coreService;
/**
- * Configuration storage for checking if enhanced exception sensor is ON.
+ * {@link TracerImpl}.
*/
@Autowired
- private IConfigurationStorage configurationStorage;
+ private TracerImpl tracer;
/**
- * The invocation sequence hook.
+ * The property accessor.
*/
- private InvocationSequenceHook invocationSequenceHook = null;
+ @Autowired
+ private IPropertyAccessor propertyAccessor;
/**
- * No-arg constructor needed for Spring.
+ * Configuration storage for checking if enhanced exception sensor is ON.
*/
- public InvocationSequenceSensor() {
- }
+ @Autowired
+ private IConfigurationStorage configurationStorage;
/**
- * The default constructor which needs 2 parameter for initialization.
- *
- * @param timer
- * The timer used for accurate measuring.
- * @param platformManager
- * The Platform manager.
- * @param propertyAccessor
- * The property accessor.
- * @param configurationStorage
- * {@link IConfigurationStorage}.
+ * The invocation sequence hook.
*/
- public InvocationSequenceSensor(Timer timer, IPlatformManager platformManager, IPropertyAccessor propertyAccessor, IConfigurationStorage configurationStorage) {
- this.timer = timer;
- this.platformManager = platformManager;
- this.propertyAccessor = propertyAccessor;
- this.configurationStorage = configurationStorage;
- }
+ private InvocationSequenceHook invocationSequenceHook = null;
/**
* {@inheritDoc}
@@ -96,7 +85,7 @@ protected void initHook(Map parameters) {
enhancedExceptionSensor = false;
}
- invocationSequenceHook = new InvocationSequenceHook(timer, platformManager, propertyAccessor, parameters, enhancedExceptionSensor);
+ invocationSequenceHook = new InvocationSequenceHook(timer, platformManager, coreService, tracer, propertyAccessor, parameters, enhancedExceptionSensor);
}
}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/RemoteClientHook.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/RemoteClientHook.java
new file mode 100644
index 000000000..253542185
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/RemoteClientHook.java
@@ -0,0 +1,154 @@
+package rocks.inspectit.agent.java.sensor.method.remote.client;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+import rocks.inspectit.agent.java.core.ICoreService;
+import rocks.inspectit.agent.java.core.IPlatformManager;
+import rocks.inspectit.agent.java.hooking.IMethodHook;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanImpl;
+import rocks.inspectit.agent.java.sensor.method.http.StartEndMarker;
+import rocks.inspectit.agent.java.tracing.core.ClientInterceptor;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientAdapterProvider;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.transformer.SpanTransformer;
+import rocks.inspectit.shared.all.tracing.data.AbstractSpan;
+
+/**
+ * The hook is the default implementation of remote client. The hook works with the
+ * {@link ClientInterceptor} in order to correctly handle client request start in the
+ * {@link #beforeBody(long, long, Object, Object[], RegisteredSensorConfig)} and request end in the
+ * {@link #secondAfterBody(ICoreService, long, long, Object, Object[], Object, RegisteredSensorConfig)}.
+ *
+ * This hook measures also measures execution time.
+ *
+ * The created spans will be passed to the give core service of the second after body method.
+ *
+ * @author Thomas Kluge
+ * @author Ivan Senic
+ *
+ */
+public class RemoteClientHook implements IMethodHook {
+
+ /**
+ * The logger of the class.
+ */
+ private static final Logger LOG = LoggerFactory.getLogger(RemoteClientHook.class);
+
+ /**
+ * Helps us to ensure that we only execute one remote client hook for each client request on all
+ * remote client sensor implementations.
+ *
+ * Static on purpose.
+ */
+ private static final StartEndMarker REF_MARKER = new StartEndMarker();
+
+ /**
+ * {@link ClientInterceptor}.
+ */
+ private final ClientInterceptor clientInterceptor;
+
+ /**
+ * {@link ClientAdapterProvider}.
+ */
+ private final ClientAdapterProvider clientAdapterProvider;
+
+ /**
+ * The Platform manager.
+ */
+ private final IPlatformManager platformManager;
+
+ /**
+ * The stack containing the span created by the {@link #clientInterceptor}.
+ */
+ private final ThreadLocal spanStack = new ThreadLocal();
+
+ /**
+ * Default constructor.
+ *
+ * @param clientInterceptor
+ * Our client interceptor.
+ * @param clientAdapterProvider
+ * {@link ClientAdapterProvider}
+ * @param platformManager
+ * The Platform manager
+ *
+ */
+ public RemoteClientHook(ClientInterceptor clientInterceptor, ClientAdapterProvider clientAdapterProvider, IPlatformManager platformManager) {
+ this.clientInterceptor = clientInterceptor;
+ this.clientAdapterProvider = clientAdapterProvider;
+ this.platformManager = platformManager;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void beforeBody(long methodId, long sensorTypeId, Object object, Object[] parameters, RegisteredSensorConfig rsc) {
+ if (!REF_MARKER.isMarkerSet()) {
+ // get requestAdapter and handle
+ ClientRequestAdapter> adapter = clientAdapterProvider.getClientRequestAdapter(object, parameters, rsc);
+ if (null != adapter) {
+ SpanImpl span = clientInterceptor.handleRequest(adapter);
+
+ if (null != span) {
+ spanStack.set(span);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Remote client hook before body span " + span);
+ }
+ }
+ }
+
+ }
+ REF_MARKER.markCall();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void firstAfterBody(long methodId, long sensorTypeId, Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc) {
+ REF_MARKER.markEndCall();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void secondAfterBody(ICoreService coreService, long methodId, long sensorTypeId, Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc) {
+ // check if in the right(first) invocation
+ if (REF_MARKER.isMarkerSet() && REF_MARKER.matchesFirst()) {
+ // call ended, remove the marker.
+ REF_MARKER.remove();
+
+ // extract span from thread local
+ SpanImpl span = spanStack.get();
+
+ if (null != span) {
+ // get requestAdapter
+ ResponseAdapter adapter = clientAdapterProvider.getClientResponseAdapter(object, parameters, result, rsc);
+ // only handle if request adapter is provided
+ if (null != adapter) {
+ clientInterceptor.handleResponse(span, adapter);
+ spanStack.remove();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Remote client hook after body span " + span);
+ }
+
+ AbstractSpan transformedSpan = SpanTransformer.transformSpan(span);
+ transformedSpan.setPlatformIdent(platformManager.getPlatformId());
+ transformedSpan.setMethodIdent(methodId);
+ transformedSpan.setSensorTypeIdent(sensorTypeId);
+
+ // add to core service (use span id as prefix)
+ coreService.addMethodSensorData(sensorTypeId, methodId, String.valueOf(transformedSpan.getSpanIdent().getId()), transformedSpan);
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/RemoteClientSensor.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/RemoteClientSensor.java
new file mode 100644
index 000000000..fbf67378b
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/RemoteClientSensor.java
@@ -0,0 +1,71 @@
+package rocks.inspectit.agent.java.sensor.method.remote.client;
+
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import rocks.inspectit.agent.java.core.IPlatformManager;
+import rocks.inspectit.agent.java.hooking.IHook;
+import rocks.inspectit.agent.java.sensor.method.AbstractMethodSensor;
+import rocks.inspectit.agent.java.tracing.core.ClientInterceptor;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientAdapterProvider;
+import rocks.inspectit.agent.java.util.ReflectionCache;
+
+/**
+ * Abstract class for all remote client sensors. Subclasses must implement
+ * {@link #getClientAdapterProvider()} that is passed to the {@link RemoteClientHook} during
+ * initialization.
+ *
+ * @author Ivan Senic
+ *
+ */
+public abstract class RemoteClientSensor extends AbstractMethodSensor {
+
+ /**
+ * One reflection cache for all instances of all remote client sensors.
+ */
+ protected static final ReflectionCache CACHE = new ReflectionCache();
+
+ /**
+ * The Platform manager.
+ */
+ @Autowired
+ private IPlatformManager platformManager;
+
+ /**
+ * Client interceptor.
+ */
+ @Autowired
+ private ClientInterceptor clientInterceptor;
+
+ /**
+ * Hook.
+ */
+ private RemoteClientHook hook;
+
+ /**
+ * Sub-classes should provide the correct requestAdapter provider based on the technology and framework
+ * they are targeting.
+ *
+ * @return {@link ClientAdapterProvider}.
+ */
+ protected abstract ClientAdapterProvider getClientAdapterProvider();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IHook getHook() {
+ return hook;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void initHook(Map parameters) {
+ ClientAdapterProvider clientAdapterProvider = getClientAdapterProvider();
+ hook = new RemoteClientHook(clientInterceptor, clientAdapterProvider, platformManager);
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/ApacheHttpClientV40Sensor.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/ApacheHttpClientV40Sensor.java
new file mode 100644
index 000000000..71da72c43
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/ApacheHttpClientV40Sensor.java
@@ -0,0 +1,66 @@
+package rocks.inspectit.agent.java.sensor.method.remote.client.http;
+
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+import rocks.inspectit.agent.java.sensor.method.remote.client.RemoteClientSensor;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientAdapterProvider;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.empty.EmptyResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.HttpClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.HttpResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpRequest;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpResponse;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl.ApacheHttpClientV40HttpClientRequest;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl.ApacheHttpClientV40HttpResponse;
+
+/**
+ * Remote client sensor for intercepting HTTP calls made with Apache HTTP client. Targeted to work
+ * with all 4.x versions of Apache HTTP client. Currently supports only sync calls.
+ *
+ * Targeted instrumentation method:
+ *
+ * - {@code org.apache.http.impl.client.CloseableHttpClient#doExecute(org.apache.http.HttpHost, org.apache.http.HttpRequest, org.apache.http.protocol.HttpContext)}
+ *
- {@code org.apache.http.client.HttpClient#execute(org.apache.http.HttpHost, org.apache.http.HttpRequest, org.apache.http.protocol.HttpContext)}
+ *
- {@code org.apache.http.client.RequestDirector#execute(org.apache.http.HttpHost, org.apache.http.HttpRequest, org.apache.http.protocol.HttpContext)}
+ *
+ *
+ * @author Ivan Senic
+ */
+public class ApacheHttpClientV40Sensor extends RemoteClientSensor implements ClientAdapterProvider {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ClientRequestAdapter getClientRequestAdapter(Object object, Object[] parameters, RegisteredSensorConfig rsc) {
+ // Apache HTTP request is second parameter
+ Object httpRequest = parameters[1];
+ HttpRequest request = new ApacheHttpClientV40HttpClientRequest(httpRequest, CACHE);
+ return new HttpClientRequestAdapter(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ResponseAdapter getClientResponseAdapter(Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc) {
+ // Apache HTTP response is result of method invocation
+ // in case of exceptions, result will be null, thus we can only use empty adapter
+ if (null != result) {
+ Object httpResponse = result;
+ HttpResponse response = new ApacheHttpClientV40HttpResponse(httpResponse, CACHE);
+ return new HttpResponseAdapter(response);
+ } else {
+ return EmptyResponseAdapter.INSTANCE;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ClientAdapterProvider getClientAdapterProvider() {
+ return this;
+ }
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/JettyHttpClientV61Sensor.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/JettyHttpClientV61Sensor.java
new file mode 100644
index 000000000..d9f8c0524
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/JettyHttpClientV61Sensor.java
@@ -0,0 +1,59 @@
+package rocks.inspectit.agent.java.sensor.method.remote.client.http;
+
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+import rocks.inspectit.agent.java.sensor.method.remote.client.RemoteClientSensor;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientAdapterProvider;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.AsyncHttpClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.HttpResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpRequest;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpResponse;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl.AsyncHttpResponse;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl.JettyHttpClientV61HttpClientRequest;
+
+/**
+ * Remote client sensor for intercepting HTTP calls made with Jetty HTTP client. Targeted to work
+ * with versions 6.x-8.x of Jetty HTTP client.
+ *
+ * Targeted instrumentation method:
+ *
+ * - {@code org.mortbay.jetty.client.HttpClient#send(org.mortbay.jetty.client.HttpExchange)}
+ *
- {@code org.eclipse.jetty.client.HttpClient#send(org.eclipse.jetty.client.HttpExchange)}
+ *
+ *
+ * @author Ivan Senic
+ */
+public class JettyHttpClientV61Sensor extends RemoteClientSensor implements ClientAdapterProvider {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ClientRequestAdapter getClientRequestAdapter(Object object, Object[] parameters, RegisteredSensorConfig rsc) {
+ // http exchange is first parameter
+ Object httpExchange = parameters[0];
+ HttpRequest request = new JettyHttpClientV61HttpClientRequest(httpExchange, CACHE);
+ return new AsyncHttpClientRequestAdapter(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ResponseAdapter getClientResponseAdapter(Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc) {
+ // async client, empty response adapter
+ HttpResponse response = AsyncHttpResponse.INSTANCE;
+ return new HttpResponseAdapter(response);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ClientAdapterProvider getClientAdapterProvider() {
+ return this;
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/SpringRestTemplateClientSensor.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/SpringRestTemplateClientSensor.java
new file mode 100644
index 000000000..f3c9c1ec6
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/SpringRestTemplateClientSensor.java
@@ -0,0 +1,75 @@
+package rocks.inspectit.agent.java.sensor.method.remote.client.http;
+
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+import rocks.inspectit.agent.java.sensor.method.remote.client.RemoteClientSensor;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientAdapterProvider;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.empty.EmptyResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.AsyncHttpClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.HttpClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.HttpResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpResponse;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl.AsyncHttpResponse;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl.SpringRestTemplateHttpClientRequest;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl.SpringRestTemplateHttpResponse;
+
+/**
+ * Remote client sensor for intercepting HTTP calls made with Spring Rest Template wrapper.
+ *
+ * Targeted instrumentation method:
+ *
+ * - {@code org.springframework.http.client.ClientHttpRequest#execute()}
+ *
- {@code org.springframework.http.client.AsyncClientHttpRequest#executeAsync()}
+ *
+ *
+ * @author Ivan Senic
+ */
+public class SpringRestTemplateClientSensor extends RemoteClientSensor implements ClientAdapterProvider {
+
+ /**
+ * Target method name for the asynchronous execution.
+ */
+ private static final String ASYNC_METHOD_NAME = "executeAsync";
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ClientAdapterProvider getClientAdapterProvider() {
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ClientRequestAdapter getClientRequestAdapter(Object object, Object[] parameters, RegisteredSensorConfig rsc) {
+ Object request = object;
+ SpringRestTemplateHttpClientRequest clientRequest = new SpringRestTemplateHttpClientRequest(request, CACHE);
+ // we know which one we are going for, can be only executeAsync or execute
+ if (ASYNC_METHOD_NAME.equals(rsc.getTargetMethodName())) {
+ return new AsyncHttpClientRequestAdapter(clientRequest);
+ } else {
+ return new HttpClientRequestAdapter(clientRequest);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ResponseAdapter getClientResponseAdapter(Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc) {
+ if (ASYNC_METHOD_NAME.equals(rsc.getTargetMethodName())) {
+ return new HttpResponseAdapter(AsyncHttpResponse.INSTANCE);
+ } else if (null != result) {
+ Object response = result;
+ HttpResponse httpResponse = new SpringRestTemplateHttpResponse(response, CACHE);
+ return new HttpResponseAdapter(httpResponse);
+ } else {
+ return EmptyResponseAdapter.INSTANCE;
+ }
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/UrlConnectionSensor.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/UrlConnectionSensor.java
new file mode 100644
index 000000000..d5ff60662
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/UrlConnectionSensor.java
@@ -0,0 +1,77 @@
+package rocks.inspectit.agent.java.sensor.method.remote.client.http;
+
+import java.net.HttpURLConnection;
+
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+import rocks.inspectit.agent.java.sensor.method.remote.client.RemoteClientSensor;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientAdapterProvider;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.HttpClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.HttpResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpRequest;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpResponse;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl.UrlConnectionHttpClientRequestResponse;
+
+/**
+ * Remote client sensor for intercepting HTTP calls made with Java's {@link HttpURLConnection}.
+ *
+ * This sensor provides the {@link ResponseAdapter} only in the
+ * {@code java.net.HttpURLConnection#getInputStream()}. This way request and response handling can
+ * be separated in two different method hooks, as the {@link HttpURLConnection} API provides such
+ * possibility.
+ *
+ * Targeted instrumentation methods:
+ *
+ * - {@code java.net.HttpURLConnection#connect()}
+ *
- {@code java.net.HttpURLConnection#getOutputStream()}
+ *
- {@code java.net.HttpURLConnection#getInputStream()}
+ *
+ *
+ * @author Ivan Senic
+ */
+public class UrlConnectionSensor extends RemoteClientSensor implements ClientAdapterProvider {
+
+ /**
+ * Name of the get input stream method.
+ */
+ public static final String GET_INPUT_STREAM_METHOD = "getInputStream";
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ClientRequestAdapter getClientRequestAdapter(Object object, Object[] parameters, RegisteredSensorConfig rsc) {
+ Object urlConnection = object;
+ if (urlConnection instanceof HttpURLConnection) {
+ HttpRequest request = new UrlConnectionHttpClientRequestResponse((HttpURLConnection) urlConnection);
+ return new HttpClientRequestAdapter(request);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ResponseAdapter getClientResponseAdapter(Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc) {
+ Object urlConnection = object;
+ if ((urlConnection instanceof HttpURLConnection) && GET_INPUT_STREAM_METHOD.equals(rsc.getTargetMethodName())) {
+ HttpResponse response = new UrlConnectionHttpClientRequestResponse((HttpURLConnection) urlConnection);
+ return new HttpResponseAdapter(response);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ClientAdapterProvider getClientAdapterProvider() {
+ return this;
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/mq/JmsRemoteClientSensor.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/mq/JmsRemoteClientSensor.java
new file mode 100644
index 000000000..b34ce1cc8
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/client/mq/JmsRemoteClientSensor.java
@@ -0,0 +1,77 @@
+package rocks.inspectit.agent.java.sensor.method.remote.client.mq;
+
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+import rocks.inspectit.agent.java.sensor.method.remote.client.RemoteClientSensor;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientAdapterProvider;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.empty.EmptyResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.mq.MQRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.mq.data.impl.JmsMessage;
+
+/**
+ * Remote client sensor for intercepting JMS message sending.
+ *
+ * Targeted instrumentation method:
+ *
+ * - {@code javax.jms.MessageProducer#send(javax.jms.Message)}
+ *
- {@code javax.jms.MessageProducer#send(javax.jms.Message int int long)}
+ *
- {@code javax.jms.MessageProducer#send(javax.jms.Queue javax.jms.Message)}
+ *
- {@code javax.jms.MessageProducer#send(javax.jms.Queue javax.jms.Message int int long)}
+ *
+ *
+ * Please note that this sensor would work with any method has the javax.jms.Message as one of the
+ * parameters, as we are figuring out the parameter index by inspecting the
+ * {@link RegisteredSensorConfig}.
+ *
+ * @author Ivan Senic
+ */
+public class JmsRemoteClientSensor extends RemoteClientSensor implements ClientAdapterProvider {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ClientAdapterProvider getClientAdapterProvider() {
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ClientRequestAdapter getClientRequestAdapter(Object object, Object[] parameters, RegisteredSensorConfig rsc) {
+ Object message = getMessage(parameters, rsc);
+ JmsMessage jmsMessage = new JmsMessage(message, CACHE);
+ return new MQRequestAdapter(jmsMessage);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ResponseAdapter getClientResponseAdapter(Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc) {
+ return EmptyResponseAdapter.INSTANCE;
+ }
+
+ /**
+ * Gets the message object from the parameters. This method will consult the
+ * {@link RegisteredSensorConfig} in order to find parameter index with the FQN of
+ * {@value JmsMessage#JAVAX_JMS_MESSAGE}.
+ *
+ * @param parameters
+ * Parameters of method invocation.
+ * @param rsc
+ * {@link RegisteredSensorConfig}
+ * @return Message object or null
if one can not be located.
+ */
+ private Object getMessage(Object[] parameters, RegisteredSensorConfig rsc) {
+ int index = rsc.getParameterTypes().indexOf(JmsMessage.JAVAX_JMS_MESSAGE);
+ if (index >= 0) {
+ return parameters[index];
+ }
+ return null;
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/RemoteServerHook.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/RemoteServerHook.java
new file mode 100644
index 000000000..05f1353aa
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/RemoteServerHook.java
@@ -0,0 +1,150 @@
+package rocks.inspectit.agent.java.sensor.method.remote.server;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+import rocks.inspectit.agent.java.core.ICoreService;
+import rocks.inspectit.agent.java.core.IPlatformManager;
+import rocks.inspectit.agent.java.hooking.IMethodHook;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanImpl;
+import rocks.inspectit.agent.java.sensor.method.http.StartEndMarker;
+import rocks.inspectit.agent.java.tracing.core.ServerInterceptor;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientAdapterProvider;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ServerAdapterProvider;
+import rocks.inspectit.agent.java.tracing.core.adapter.ServerRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.transformer.SpanTransformer;
+import rocks.inspectit.shared.all.tracing.data.AbstractSpan;
+
+/**
+ * The hook is the default implementation of remote server. The hook works with the
+ * {@link ServerInterceptor} in order to correctly handle server request start in the
+ * {@link #beforeBody(long, long, Object, Object[], RegisteredSensorConfig)} and request end in the
+ * {@link #secondAfterBody(ICoreService, long, long, Object, Object[], Object, RegisteredSensorConfig)}.
+ *
+ * This hook measures also measures execution time.
+ *
+ * The created spans will be passed to the give core service of the second after body method.
+ *
+ * @author Thomas Kluge
+ * @author Ivan Senic
+ *
+ */
+public class RemoteServerHook implements IMethodHook {
+
+ /**
+ * The logger of the class.
+ */
+ private static final Logger LOG = LoggerFactory.getLogger(RemoteServerHook.class);
+
+ /**
+ * Helps us to ensure that we only execute one remote server hook for each server request on all
+ * remote server sensor implementations.
+ *
+ * Static on purpose.
+ */
+ private static final StartEndMarker REF_MARKER = new StartEndMarker();
+
+ /**
+ * {@link ServerInterceptor}.
+ */
+ private final ServerInterceptor serverInterceptor;
+
+ /**
+ * {@link ClientAdapterProvider}.
+ */
+ private final ServerAdapterProvider serverAdapterProvider;
+
+ /**
+ * The Platform manager.
+ */
+ private final IPlatformManager platformManager;
+
+ /**
+ * The stack containing the created span by this sensor.
+ */
+ private final ThreadLocal spanStack = new ThreadLocal();
+
+ /**
+ * Default constructor.
+ *
+ * @param serverInterceptor
+ * Our server interceptor.
+ * @param serverAdapterProvider
+ * {@link ServerAdapterProvider}
+ * @param platformManager
+ * The Platform manager
+ *
+ */
+ public RemoteServerHook(ServerInterceptor serverInterceptor, ServerAdapterProvider serverAdapterProvider, IPlatformManager platformManager) {
+ this.serverInterceptor = serverInterceptor;
+ this.serverAdapterProvider = serverAdapterProvider;
+ this.platformManager = platformManager;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void beforeBody(long methodId, long sensorTypeId, Object object, Object[] parameters, RegisteredSensorConfig rsc) {
+ if (!REF_MARKER.isMarkerSet()) {
+ // get requestAdapter and handle
+ ServerRequestAdapter> adapter = serverAdapterProvider.getServerRequestAdapter(object, parameters, rsc);
+ SpanImpl span = serverInterceptor.handleRequest(adapter);
+
+ if (null != span) {
+ spanStack.set(span);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Remote server hook before body span " + span);
+ }
+ }
+
+ }
+ REF_MARKER.markCall();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void firstAfterBody(long methodId, long sensorTypeId, Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc) {
+ REF_MARKER.markEndCall();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void secondAfterBody(ICoreService coreService, long methodId, long sensorTypeId, Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc) {
+ // check if in the right(first) invocation
+ if (REF_MARKER.isMarkerSet() && REF_MARKER.matchesFirst()) {
+ // call ended, remove the marker.
+ REF_MARKER.remove();
+
+ // extract span from thread local
+ SpanImpl span = spanStack.get();
+ spanStack.remove();
+
+ if (null != span) {
+ // get requestAdapter and handle
+ ResponseAdapter responseAdapter = serverAdapterProvider.getServerResponseAdapter(object, parameters, result, rsc);
+ serverInterceptor.handleResponse(span, responseAdapter);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Remote server hook after body span " + span);
+ }
+
+ AbstractSpan transformedSpan = SpanTransformer.transformSpan(span);
+ transformedSpan.setPlatformIdent(platformManager.getPlatformId());
+ transformedSpan.setMethodIdent(methodId);
+ transformedSpan.setSensorTypeIdent(sensorTypeId);
+
+ // add to core service (use span id as prefix)
+ coreService.addMethodSensorData(sensorTypeId, methodId, String.valueOf(transformedSpan.getSpanIdent().getId()), transformedSpan);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/RemoteServerSensor.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/RemoteServerSensor.java
new file mode 100644
index 000000000..b398d3077
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/RemoteServerSensor.java
@@ -0,0 +1,71 @@
+package rocks.inspectit.agent.java.sensor.method.remote.server;
+
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import rocks.inspectit.agent.java.core.IPlatformManager;
+import rocks.inspectit.agent.java.hooking.IHook;
+import rocks.inspectit.agent.java.sensor.method.AbstractMethodSensor;
+import rocks.inspectit.agent.java.tracing.core.ServerInterceptor;
+import rocks.inspectit.agent.java.tracing.core.adapter.ServerAdapterProvider;
+import rocks.inspectit.agent.java.util.ReflectionCache;
+
+/**
+ * Abstract class for all remote server sensors that can read tracing information on the start of
+ * the request. Subclasses must implement {@link #getServerAdapterProvider()} that is passed to the
+ * {@link RemoteServerHook} during initialization.
+ *
+ * @author Ivan Senic
+ *
+ */
+public abstract class RemoteServerSensor extends AbstractMethodSensor {
+
+ /**
+ * One reflection cache for all instances of all remote server sensors.
+ */
+ protected static final ReflectionCache CACHE = new ReflectionCache();
+
+ /**
+ * The Platform manager.
+ */
+ @Autowired
+ private IPlatformManager platformManager;
+
+ /**
+ * Server interceptor.
+ */
+ @Autowired
+ private ServerInterceptor serverInterceptor;
+
+ /**
+ * Hook.
+ */
+ private RemoteServerHook hook;
+
+ /**
+ * Sub-classes should provide the correct requestAdapter provider based on the technology and framework
+ * they are targeting.
+ *
+ * @return {@link ServerAdapterProvider}.
+ */
+ protected abstract ServerAdapterProvider getServerAdapterProvider();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IHook getHook() {
+ return hook;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void initHook(Map parameters) {
+ ServerAdapterProvider serverAdapterProvider = getServerAdapterProvider();
+ hook = new RemoteServerHook(serverInterceptor, serverAdapterProvider, platformManager);
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/http/JavaHttpRemoteServerSensor.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/http/JavaHttpRemoteServerSensor.java
new file mode 100644
index 000000000..d706fe39d
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/http/JavaHttpRemoteServerSensor.java
@@ -0,0 +1,58 @@
+package rocks.inspectit.agent.java.sensor.method.remote.server.http;
+
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+import rocks.inspectit.agent.java.sensor.method.remote.server.RemoteServerSensor;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ServerAdapterProvider;
+import rocks.inspectit.agent.java.tracing.core.adapter.ServerRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.HttpResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.HttpServerRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl.JavaHttpResponse;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl.JavaHttpServerRequest;
+
+/**
+ * Remote server sensor for intercepting HTTP calls using java HTTP servlet request and response.
+ *
+ * Targeted instrumentation method:
+ *
+ * - {@code javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)}
+ *
- {@code javax.servlet.FilterChain#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)}
+ *
- {@code javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}
+ *
+ *
+ * @author Ivan Senic
+ */
+public class JavaHttpRemoteServerSensor extends RemoteServerSensor implements ServerAdapterProvider {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ServerAdapterProvider getServerAdapterProvider() {
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ServerRequestAdapter getServerRequestAdapter(Object object, Object[] parameters, RegisteredSensorConfig rsc) {
+ // request is first parameter
+ Object httpServletRequest = parameters[0];
+ JavaHttpServerRequest serverRequest = new JavaHttpServerRequest(httpServletRequest, CACHE);
+ return new HttpServerRequestAdapter(serverRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ResponseAdapter getServerResponseAdapter(Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc) {
+ // response is second parameter
+ Object httpServletResponse = parameters[1];
+ JavaHttpResponse httpResponse = new JavaHttpResponse(httpServletResponse, CACHE);
+ return new HttpResponseAdapter(httpResponse);
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/manual/ManualRemoteServerSensor.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/manual/ManualRemoteServerSensor.java
new file mode 100644
index 000000000..98c2d38f0
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/manual/ManualRemoteServerSensor.java
@@ -0,0 +1,44 @@
+package rocks.inspectit.agent.java.sensor.method.remote.server.manual;
+
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+import rocks.inspectit.agent.java.sensor.method.remote.server.RemoteServerSensor;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ServerAdapterProvider;
+import rocks.inspectit.agent.java.tracing.core.adapter.ServerRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.empty.EmptyRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.empty.EmptyResponseAdapter;
+
+/**
+ * Remote server sensor that users can manually place on any method. Not depending on any technology
+ * and can not receive any tracing data.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class ManualRemoteServerSensor extends RemoteServerSensor implements ServerAdapterProvider {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ServerAdapterProvider getServerAdapterProvider() {
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ServerRequestAdapter> getServerRequestAdapter(Object object, Object[] parameters, RegisteredSensorConfig rsc) {
+ return EmptyRequestAdapter.INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ResponseAdapter getServerResponseAdapter(Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc) {
+ return EmptyResponseAdapter.INSTANCE;
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/mq/JmsListenerRemoteServerSensor.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/mq/JmsListenerRemoteServerSensor.java
new file mode 100644
index 000000000..fdd5b7df7
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/remote/server/mq/JmsListenerRemoteServerSensor.java
@@ -0,0 +1,54 @@
+package rocks.inspectit.agent.java.sensor.method.remote.server.mq;
+
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+import rocks.inspectit.agent.java.sensor.method.remote.server.RemoteServerSensor;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ServerAdapterProvider;
+import rocks.inspectit.agent.java.tracing.core.adapter.ServerRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.empty.EmptyResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.mq.MQRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.mq.data.impl.JmsMessage;
+
+/**
+ * Remote server sensor for intercepting JMS message receiving. This sensor is intended to work with
+ * JMS listener, but in theory can be placed on any method where JMS message is first parameter.
+ *
+ * Targeted instrumentation method:
+ *
+ * - {@code javax.jms.MessageListener#onMessage(javax.jms.Message)}
+ *
- {@code javax.jms.MessageListener#onMessage(javax.jms.Message, javax.jms.Session)}
+ *
+ *
+ * @author Ivan Senic
+ */
+public class JmsListenerRemoteServerSensor extends RemoteServerSensor implements ServerAdapterProvider {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ServerAdapterProvider getServerAdapterProvider() {
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ServerRequestAdapter getServerRequestAdapter(Object object, Object[] parameters, RegisteredSensorConfig rsc) {
+ // message is first parameter
+ Object message = parameters[0];
+ JmsMessage jmsMessage = new JmsMessage(message, CACHE);
+ return new MQRequestAdapter(jmsMessage);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ResponseAdapter getServerResponseAdapter(Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc) {
+ return EmptyResponseAdapter.INSTANCE;
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/special/ClassLoadingDelegationHook.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/special/ClassLoadingDelegationHook.java
index 2d0ff6498..3291fdffb 100644
--- a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/special/ClassLoadingDelegationHook.java
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/sensor/method/special/ClassLoadingDelegationHook.java
@@ -103,7 +103,7 @@ private Class> loadClass(Object classLoader, String className) {
* @return True if we should try to load this with the inspectIT class loader.
*/
private boolean loadWithInspectItClassLoader(Object classLoader, String className) {
- return ClassLoadingUtil.isInspectITClass(className) && !REFLECTASM_ACCESS_CLASS_LOADER_FQN.equals(classLoader.getClass().getName());
+ return (ClassLoadingUtil.isInspectITClass(className) || ClassLoadingUtil.isOpenTracingClass(className)) && !REFLECTASM_ACCESS_CLASS_LOADER_FQN.equals(classLoader.getClass().getName());
}
/**
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/spring/SpringConfiguration.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/spring/SpringConfiguration.java
index 3cd0853ba..f654b3ebd 100644
--- a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/spring/SpringConfiguration.java
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/spring/SpringConfiguration.java
@@ -25,6 +25,9 @@
import rocks.inspectit.agent.java.IThreadTransformHelper;
import rocks.inspectit.agent.java.config.IConfigurationStorage;
import rocks.inspectit.agent.java.connection.impl.AgentAwareClient;
+import rocks.inspectit.agent.java.sdk.opentracing.Reporter;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.TracerImpl;
+import rocks.inspectit.agent.java.sdk.opentracing.util.SystemTimer;
import rocks.inspectit.agent.java.util.AgentAwareThread;
import rocks.inspectit.shared.all.instrumentation.config.impl.AbstractSensorTypeConfig;
import rocks.inspectit.shared.all.instrumentation.config.impl.JmxSensorTypeConfig;
@@ -140,6 +143,21 @@ public Client getClient(PrototypesProvider prototypesProvider, IThreadTransformH
return new AgentAwareClient(serialization, prototypesProvider, threadTransformHelper);
}
+ /**
+ * Creates the {@link TracerImpl}.
+ *
+ * @param reporter
+ * Reporter to use. Autowired.
+ * @return Created bean
+ */
+ @Bean
+ @Scope(BeanDefinition.SCOPE_SINGLETON)
+ @Autowired
+ public TracerImpl getTracer(Reporter reporter) {
+ TracerImpl tracer = new TracerImpl(new SystemTimer(), reporter, true);
+ return tracer;
+ }
+
/**
* Registers components needed by the configuration to the Spring container.
*
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/ClientInterceptor.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/ClientInterceptor.java
new file mode 100644
index 000000000..69908e262
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/ClientInterceptor.java
@@ -0,0 +1,120 @@
+package rocks.inspectit.agent.java.tracing.core;
+
+import java.util.Map;
+
+import org.apache.commons.collections.MapUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import io.opentracing.tag.Tags;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanBuilderImpl;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanImpl;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.TracerImpl;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.shared.all.tracing.constants.ExtraTags;
+
+/**
+ * Client interceptor provide easy way to handle client request and response. It internally calls
+ * {@link TracerImpl} to correctly create new client spans. To use the interceptor one must provide
+ * {@link ClientRequestAdapter} to handle the request and {@link ResponseAdapter} to handle the
+ * response.
+ *
+ * @author Ivan Senic
+ *
+ */
+@Component
+public class ClientInterceptor {
+
+ /**
+ * {@link TracerImpl}.
+ */
+ private final TracerImpl tracer;
+
+ /**
+ * Default constructor.
+ *
+ * @param tracer
+ * {@link TracerImpl} to use.
+ */
+ @Autowired
+ public ClientInterceptor(TracerImpl tracer) {
+ this.tracer = tracer;
+ }
+
+ /**
+ * Handles the {@link ClientRequestAdapter}. This method should be called when new client
+ * request is created.
+ *
+ * @param
+ * type of carrier adapter is providing
+ * @param requestAdapter
+ * {@link ClientRequestAdapter} providing necessary information.
+ * @return Created span
+ */
+ public SpanImpl handleRequest(ClientRequestAdapter requestAdapter) {
+ if (!requestAdapter.startClientSpan()) {
+ return null;
+ }
+
+ // create span from the current context
+ // using null for operation name as we can distinguish spans without it
+ SpanBuilderImpl builder = tracer.buildSpan(null, requestAdapter.getReferenceType(), true);
+
+ // set as client
+ builder.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT);
+
+ // set no reporting as we will do it ourselves
+ builder.doNotReport();
+
+ // set propagation type
+ if (null != requestAdapter.getPropagationType()) {
+ builder.withTag(ExtraTags.PROPAGATION_TYPE, requestAdapter.getPropagationType().toString());
+ }
+
+ // handle custom tags
+ Map tags = requestAdapter.getTags();
+ if (MapUtils.isNotEmpty(tags)) {
+ for (Map.Entry e : tags.entrySet()) {
+ builder.withTag(e.getKey(), e.getValue());
+ }
+ }
+
+ // start
+ SpanImpl span = builder.start();
+
+ // inject here as the context is created when span is started
+ tracer.inject(span.context(), requestAdapter.getFormat(), requestAdapter.getCarrier());
+
+ return span;
+ }
+
+ /**
+ * Handles the {@link ResponseAdapter}. This method should be called when the response on the
+ * client is received.
+ *
+ * @param span
+ * Span to finish.
+ * @param responseAdapter
+ * {@link ResponseAdapter} providing necessary information.
+ * @return Finished span (same as given parameter instance).
+ */
+ public SpanImpl handleResponse(SpanImpl span, ResponseAdapter responseAdapter) {
+ if (null == span) {
+ return null;
+ }
+
+ // handle tags
+ Map tags = responseAdapter.getTags();
+ if (MapUtils.isNotEmpty(tags)) {
+ for (Map.Entry e : tags.entrySet()) {
+ span.setTag(e.getKey(), e.getValue());
+ }
+ }
+
+ // finish and return
+ span.finish();
+ return span;
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/CoreServiceReporter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/CoreServiceReporter.java
new file mode 100644
index 000000000..1e5e3f644
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/CoreServiceReporter.java
@@ -0,0 +1,52 @@
+package rocks.inspectit.agent.java.tracing.core;
+
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import rocks.inspectit.agent.java.core.ICoreService;
+import rocks.inspectit.agent.java.core.IPlatformManager;
+import rocks.inspectit.agent.java.sdk.opentracing.Reporter;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanImpl;
+import rocks.inspectit.agent.java.tracing.core.transformer.SpanTransformer;
+import rocks.inspectit.shared.all.spring.logger.Log;
+import rocks.inspectit.shared.all.tracing.data.AbstractSpan;
+
+/**
+ * Tracing reporter that sends spans to the {@link ICoreService}.
+ *
+ * @author Ivan Senic
+ *
+ */
+@Component
+public class CoreServiceReporter implements Reporter {
+
+ /**
+ * Logger for this class.
+ */
+ @Log
+ Logger log;
+
+ /**
+ * {@link ICoreService}.
+ */
+ @Autowired
+ private ICoreService coreService;
+
+ /**
+ * {@link IPlatformManager}.
+ */
+ @Autowired
+ private IPlatformManager platformManager;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void report(SpanImpl span) {
+ AbstractSpan transformed = SpanTransformer.transformSpan(span);
+ transformed.setPlatformIdent(platformManager.getPlatformId());
+ coreService.addMethodSensorData(0, 0, String.valueOf(transformed.getSpanIdent().getId()), transformed);
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/ServerInterceptor.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/ServerInterceptor.java
new file mode 100644
index 000000000..b604a83f8
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/ServerInterceptor.java
@@ -0,0 +1,117 @@
+package rocks.inspectit.agent.java.tracing.core;
+
+import java.util.Map;
+
+import org.apache.commons.collections.MapUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import io.opentracing.SpanContext;
+import io.opentracing.Tracer;
+import io.opentracing.tag.Tags;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanBuilderImpl;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanImpl;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.TracerImpl;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ServerRequestAdapter;
+import rocks.inspectit.shared.all.tracing.constants.ExtraTags;
+
+/**
+ * Server interceptor provide easy way to handle server request and response. It internally calls
+ * {@link Tracer} to correctly create or update current span on the server. To use the interceptor
+ * one must provide {@link ServerRequestAdapter} to handle the request and {@link ResponseAdapter}
+ * to handle the response.
+ *
+ * @author Ivan Senic
+ *
+ */
+@Component
+public class ServerInterceptor {
+
+ /**
+ * {@link TracerImpl}.
+ */
+ @Autowired
+ private final TracerImpl tracer;
+
+ /**
+ * Default constructor.
+ *
+ * @param tracer
+ * {@link TracerImpl} to use.
+ */
+ @Autowired
+ public ServerInterceptor(TracerImpl tracer) {
+ this.tracer = tracer;
+ }
+
+ /**
+ * Handles the {@link ServerRequestAdapter}. This method should be called when new server
+ * request is received.
+ *
+ * @param
+ * type of carrier adapter is providing
+ * @param requestAdapter
+ * {@link ServerRequestAdapter} providing necessary information.
+ * @return Current span that was created.
+ */
+ public SpanImpl handleRequest(ServerRequestAdapter requestAdapter) {
+ // eject data from request
+ SpanContext context = tracer.extract(requestAdapter.getFormat(), requestAdapter.getCarrier());
+
+ // add reference to the passed context
+ // not specifying operation name as we can distinguish spans without it
+ SpanBuilderImpl builder = tracer.buildSpan();
+ builder.asChildOf(context);
+
+ // set as server
+ builder.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER);
+
+ // set no reporting as we will do it ourselves
+ builder.doNotReport();
+
+ // set propagation type
+ if (null != requestAdapter.getPropagationType()) {
+ builder.withTag(ExtraTags.PROPAGATION_TYPE, requestAdapter.getPropagationType().toString());
+ }
+
+ // handle custom tags
+ Map tags = requestAdapter.getTags();
+ if (MapUtils.isNotEmpty(tags)) {
+ for (Map.Entry e : tags.entrySet()) {
+ builder.withTag(e.getKey(), e.getValue());
+ }
+ }
+
+ return builder.start();
+ }
+
+ /**
+ * Handles the {@link ResponseAdapter}. This method should be called before sending the response
+ * on the server.
+ *
+ * @param span
+ * Span to finish.
+ * @param responseAdapter
+ * {@link ResponseAdapter} providing necessary information. Can not be
+ * null
.
+ * @return Finished span (same as given parameter instance).
+ */
+ public SpanImpl handleResponse(SpanImpl span, ResponseAdapter responseAdapter) {
+ if (null == span) {
+ return null;
+ }
+
+ // handle tags
+ Map tags = responseAdapter.getTags();
+ if (MapUtils.isNotEmpty(tags)) {
+ for (Map.Entry e : tags.entrySet()) {
+ span.setTag(e.getKey(), e.getValue());
+ }
+ }
+
+ // finish and return
+ span.finish();
+ return span;
+ }
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/Slf4jTracerLogger.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/Slf4jTracerLogger.java
new file mode 100644
index 000000000..5e8ea4b96
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/Slf4jTracerLogger.java
@@ -0,0 +1,122 @@
+package rocks.inspectit.agent.java.tracing.core;
+
+import org.slf4j.Logger;
+
+import rocks.inspectit.agent.java.sdk.opentracing.internal.TracerLogger;
+
+/**
+ * Tracer logger that uses SLF4J for logging the tracing related messages.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class Slf4jTracerLogger implements TracerLogger {
+
+ /**
+ * SLF4J Logger.
+ */
+ private Logger logger;
+
+ /**
+ * Default constructor.
+ *
+ * @param logger
+ * SLF4J logger to delegate calls to
+ */
+ public Slf4jTracerLogger(Logger logger) {
+ this.logger = logger;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isInfoEnabled() {
+ return logger.isInfoEnabled();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void info(String message) {
+ if (logger.isInfoEnabled()) {
+ logger.info(message);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isDebugEnabled() {
+ return logger.isDebugEnabled();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void debug(String message) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(message);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isWarnEnabled() {
+ return logger.isWarnEnabled();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void warn(String message) {
+ if (logger.isWarnEnabled()) {
+ logger.warn(message);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void warn(String message, Throwable t) {
+ if (logger.isWarnEnabled()) {
+ logger.warn(message, t);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isErrorEnabled() {
+ return logger.isErrorEnabled();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void error(String message) {
+ if (logger.isErrorEnabled()) {
+ logger.error(message);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void error(String message, Throwable t) {
+ if (logger.isErrorEnabled()) {
+ logger.error(message, t);
+ }
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/Slf4jTracerLoggerProvider.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/Slf4jTracerLoggerProvider.java
new file mode 100644
index 000000000..e23c52586
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/Slf4jTracerLoggerProvider.java
@@ -0,0 +1,24 @@
+package rocks.inspectit.agent.java.tracing.core;
+
+import org.slf4j.LoggerFactory;
+
+import rocks.inspectit.agent.java.sdk.opentracing.internal.TracerLogger;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.TracerLoggerProvider;
+
+/**
+ * Provider of the {@link Slf4jTracerLogger}s.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class Slf4jTracerLoggerProvider implements TracerLoggerProvider {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public TracerLogger getTraceLogger(Class> clazz) {
+ return new Slf4jTracerLogger(LoggerFactory.getLogger(clazz));
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ClientAdapterProvider.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ClientAdapterProvider.java
new file mode 100644
index 000000000..63c6d6d05
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ClientAdapterProvider.java
@@ -0,0 +1,44 @@
+package rocks.inspectit.agent.java.tracing.core.adapter;
+
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+
+/**
+ * Client adapter provider can deliver {@link ClientRequestAdapter} and {@link ResponseAdapter}
+ * based on the method invocation object, parameters and the result.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface ClientAdapterProvider {
+
+ /**
+ * Returns {@link ClientRequestAdapter} to handle the request. Implementor should used passed
+ * object and parameters in order to create correct requestAdapter.
+ *
+ * @param object
+ * Object on with method was invoked.
+ * @param parameters
+ * Method invocation parameters.
+ * @param rsc
+ * {@link RegisteredSensorConfig}.
+ * @return {@link ClientRequestAdapter}.
+ */
+ ClientRequestAdapter> getClientRequestAdapter(Object object, Object[] parameters, RegisteredSensorConfig rsc);
+
+ /**
+ * Returns {@link ResponseAdapter} to handle the request. Implementor should used passed object
+ * and parameters in order to create correct requestAdapter.
+ *
+ * @param object
+ * Object on with method was invoked.
+ * @param parameters
+ * Method invocation parameters.
+ * @param result
+ * Result of the method invocation.
+ * @param rsc
+ * {@link RegisteredSensorConfig}.
+ * @return {@link ClientRequestAdapter}.
+ */
+ ResponseAdapter getClientResponseAdapter(Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc);
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ClientRequestAdapter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ClientRequestAdapter.java
new file mode 100644
index 000000000..7f92ad20b
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ClientRequestAdapter.java
@@ -0,0 +1,52 @@
+package rocks.inspectit.agent.java.tracing.core.adapter;
+
+import io.opentracing.propagation.Format;
+
+/**
+ * Client request adapter that works together with the
+ * {@link rocks.inspectit.agent.java.tracing.core.ClientInterceptor} in order to correctly mark
+ * client request. The requestAdapter is responsible providing open tracing format and carrier, as
+ * well as for providing the basic information about the request as propagation type, reference type
+ * and tags associated with the request.
+ *
+ * This class is inspired by the Zipkin/Brave implementation, but is adapted to our needs.
+ *
+ * @param
+ * type of the carrier provided by this adapter
+ * @author Ivan Senic
+ *
+ */
+public interface ClientRequestAdapter extends RequestAdapter {
+
+ /**
+ * Defines if the new span should be started. Adapters can decide based on some information to
+ * avoid starting a new span. Example can be UrlConnection, where span should not be started if
+ * the connection has already been established.
+ *
+ * @return If new span should be started.
+ */
+ boolean startClientSpan();
+
+ /**
+ * Returns reference type for the request. If request is synchronous it's expected that
+ * {@link io.opentracing.References#CHILD_OF} is returned. If request is asynchronous it's
+ * expected that {@link io.opentracing.References#FOLLOWS_FROM} is returned.
+ *
+ * @return Returns reference type for the request.
+ */
+ String getReferenceType();
+
+ /**
+ * Format that the carrier supports.
+ *
+ * @return Format that the carrier supports.
+ */
+ Format getFormat();
+
+ /**
+ * Carrier for injecting the baggage.
+ *
+ * @return carrier
+ */
+ C getCarrier();
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/RequestAdapter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/RequestAdapter.java
new file mode 100644
index 000000000..3e7575155
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/RequestAdapter.java
@@ -0,0 +1,23 @@
+package rocks.inspectit.agent.java.tracing.core.adapter;
+
+import rocks.inspectit.shared.all.tracing.data.PropagationType;
+
+/**
+ * Generic request requestAdapter. The requestAdapter is responsible providing basic information
+ * about the request as propagation type and tags associated with the request.
+ *
+ * This class is inspired by the Zipkin/Brave implementation, but is adapted to our needs.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface RequestAdapter extends TagsProvidingAdapter {
+
+ /**
+ * Returns propagation type for the request.
+ *
+ * @return Returns propagation type for the request.
+ */
+ PropagationType getPropagationType();
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ResponseAdapter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ResponseAdapter.java
new file mode 100644
index 000000000..f0bc59ad4
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ResponseAdapter.java
@@ -0,0 +1,17 @@
+package rocks.inspectit.agent.java.tracing.core.adapter;
+
+/**
+ * Response requestAdapter works together with the
+ * {@link rocks.inspectit.agent.java.tracing.core.ClientInterceptor} or
+ * {@link rocks.inspectit.agent.java.tracing.core.ServerInterceptor} in order to correctly mark
+ * response receiving or client or server. The requestAdapter is only responsible for providing tags
+ * associated with the response.
+ *
+ * This class is inspired by the Zipkin/Brave implementation, but is adapted to our needs.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface ResponseAdapter extends TagsProvidingAdapter {
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ServerAdapterProvider.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ServerAdapterProvider.java
new file mode 100644
index 000000000..1ad4edb6f
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ServerAdapterProvider.java
@@ -0,0 +1,45 @@
+package rocks.inspectit.agent.java.tracing.core.adapter;
+
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+
+/**
+ * Server adapter provider can deliver {@link ServerRequestAdapter} and {@link ResponseAdapter}
+ * based on the method invocation object, parameters and the result.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface ServerAdapterProvider {
+
+ /**
+ * Returns {@link ServerRequestAdapter} to handle the request when the one is available on the
+ * start of the instrumented method. Implementor should used passed object and parameters in
+ * order to create correct requestAdapter.
+ *
+ * @param object
+ * Object on with method was invoked.
+ * @param parameters
+ * Method invocation parameters.
+ * @param rsc
+ * {@link RegisteredSensorConfig}.
+ * @return {@link ServerRequestAdapter}.
+ */
+ ServerRequestAdapter> getServerRequestAdapter(Object object, Object[] parameters, RegisteredSensorConfig rsc);
+
+ /**
+ * Returns {@link ResponseAdapter} to handle the request. Implementor should used passed object
+ * and parameters in order to create correct requestAdapter.
+ *
+ * @param object
+ * Object on with method was invoked.
+ * @param parameters
+ * Method invocation parameters.
+ * @param result
+ * Result of the method invocation.
+ * @param rsc
+ * {@link RegisteredSensorConfig}.
+ * @return {@link ClientRequestAdapter}.
+ */
+ ResponseAdapter getServerResponseAdapter(Object object, Object[] parameters, Object result, RegisteredSensorConfig rsc);
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ServerRequestAdapter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ServerRequestAdapter.java
new file mode 100644
index 000000000..8dcd3b3b3
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/ServerRequestAdapter.java
@@ -0,0 +1,35 @@
+package rocks.inspectit.agent.java.tracing.core.adapter;
+
+import io.opentracing.propagation.Format;
+
+/**
+ * Server request requestAdapter works together with the
+ * {@link rocks.inspectit.agent.java.tracing.core.ServerInterceptor} in order to correctly retrieve
+ * data from the server request. The requestAdapter is responsible for providing opentracing format
+ * and carrier that can provide baggage that is propagated as well as for providing the basic
+ * information about the request as propagation type and tags associated with the request.
+ *
+ * This class is inspired by the Zipkin/Brave implementation, but is adapted to our needs.
+ *
+ * @param
+ * type of the carrier provided by this adapter
+ * @author Ivan Senic
+ *
+ */
+public interface ServerRequestAdapter extends RequestAdapter {
+
+ /**
+ * Format that the carrier supports.
+ *
+ * @return Format that the carrier supports.
+ */
+ Format getFormat();
+
+ /**
+ * Carrier for extracting the baggage.
+ *
+ * @return carrier
+ */
+ C getCarrier();
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/TagsProvidingAdapter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/TagsProvidingAdapter.java
new file mode 100644
index 000000000..be954e17a
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/TagsProvidingAdapter.java
@@ -0,0 +1,22 @@
+package rocks.inspectit.agent.java.tracing.core.adapter;
+
+import java.util.Map;
+
+/**
+ * Adapter that can provide tags. Tags can be anything that describe more the client/server request
+ * or response. For example it can be HTTP URI and method invoked for the request, or status code
+ * for the response. Different adapters will provide different types of the tags based on the
+ * technology that they support.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface TagsProvidingAdapter {
+
+ /**
+ * Return tags available to this request/response adapter.
+ *
+ * @return Return tags available to this request/response adapter.
+ */
+ Map getTags();
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/empty/EmptyRequestAdapter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/empty/EmptyRequestAdapter.java
new file mode 100644
index 000000000..b6a45587a
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/empty/EmptyRequestAdapter.java
@@ -0,0 +1,85 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.empty;
+
+import java.util.Collections;
+import java.util.Map;
+
+import io.opentracing.propagation.Format;
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ServerRequestAdapter;
+import rocks.inspectit.shared.all.tracing.data.PropagationType;
+
+/**
+ * Empty request adapter can be used as {@link ServerRequestAdapter} or
+ * {@link ClientRequestAdapter}. It provides no information and it's baggage carriers can not inject
+ * nor extract data.
+ *
+ * This adapter is used for the
+ * {@link rocks.inspectit.agent.java.sensor.method.remote.server.manual.ManualRemoteServerSensor},
+ * since this type of the sensor is manually placed by the user and we can not provide any data.
+ *
+ * @author Ivan Senic
+ *
+ */
+public final class EmptyRequestAdapter implements ServerRequestAdapter, ClientRequestAdapter {
+
+ /**
+ * Static instance for usage.
+ */
+ public static final EmptyRequestAdapter INSTANCE = new EmptyRequestAdapter();
+
+ /**
+ * Private constructor, use {@link #INSTANCE}.
+ */
+ private EmptyRequestAdapter() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean startClientSpan() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PropagationType getPropagationType() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getReferenceType() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Map getTags() {
+ return Collections.emptyMap();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Format getFormat() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public TextMap getCarrier() {
+ return null;
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/empty/EmptyResponseAdapter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/empty/EmptyResponseAdapter.java
new file mode 100644
index 000000000..aeb6a1495
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/empty/EmptyResponseAdapter.java
@@ -0,0 +1,38 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.empty;
+
+import java.util.Collections;
+import java.util.Map;
+
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+
+/**
+ * {@link ResponseAdapter} that does not provide any information (tags).
+ *
+ * This adapter is used for the asynchronous responses as in this case thread is not waiting for the
+ * response and can not provide any information on it success or data.
+ *
+ * @author Ivan Senic
+ *
+ */
+public final class EmptyResponseAdapter implements ResponseAdapter {
+
+ /**
+ * Static instance for usage.
+ */
+ public static final EmptyResponseAdapter INSTANCE = new EmptyResponseAdapter();
+
+ /**
+ * Private constructor, use {@link #INSTANCE}.
+ */
+ private EmptyResponseAdapter() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Map getTags() {
+ return Collections.emptyMap();
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/AsyncHttpClientRequestAdapter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/AsyncHttpClientRequestAdapter.java
new file mode 100644
index 000000000..f5f9bcf7b
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/AsyncHttpClientRequestAdapter.java
@@ -0,0 +1,35 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http;
+
+import io.opentracing.References;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpRequest;
+
+/**
+ * The {@link ClientRequestAdapter} for all asynchronous HTTP client requests.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class AsyncHttpClientRequestAdapter extends HttpClientRequestAdapter {
+
+ /**
+ * Default constructor.
+ *
+ * @param httpClientRequest
+ * HTTP request from which we can read data.
+ */
+ public AsyncHttpClientRequestAdapter(HttpRequest httpClientRequest) {
+ super(httpClientRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Async requests are always having type of {@link io.opentracing.ReferenceType#FOLLOWS_FROM}.
+ */
+ @Override
+ public String getReferenceType() {
+ return References.FOLLOWS_FROM;
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/HttpClientRequestAdapter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/HttpClientRequestAdapter.java
new file mode 100644
index 000000000..8925b327f
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/HttpClientRequestAdapter.java
@@ -0,0 +1,66 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http;
+
+import io.opentracing.References;
+import io.opentracing.propagation.Format;
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpRequest;
+
+/**
+ * The {@link ClientRequestAdapter} for all synchronous HTTP client requests.
+ *
+ * @author Ivan Senic
+ */
+public class HttpClientRequestAdapter extends HttpRequestAdapter implements ClientRequestAdapter {
+
+ /**
+ * Http request providing data we need.
+ */
+ private HttpRequest httpRequest;
+
+ /**
+ * Default constructor.
+ *
+ * @param httpRequest
+ * HTTP request from which we can read data.
+ */
+ public HttpClientRequestAdapter(HttpRequest httpRequest) {
+ super(httpRequest);
+ this.httpRequest = httpRequest;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean startClientSpan() {
+ return httpRequest.startClientSpan();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * As this requestAdapter supports only sync HTTP calls we use {@value References.CHILD_OF}.
+ */
+ @Override
+ public String getReferenceType() {
+ return References.CHILD_OF;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Format getFormat() {
+ return Format.Builtin.HTTP_HEADERS;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public TextMap getCarrier() {
+ return httpRequest;
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/HttpRequestAdapter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/HttpRequestAdapter.java
new file mode 100644
index 000000000..5d64a4300
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/HttpRequestAdapter.java
@@ -0,0 +1,65 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import io.opentracing.tag.Tags;
+import rocks.inspectit.agent.java.tracing.core.adapter.RequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpRequest;
+import rocks.inspectit.shared.all.tracing.data.PropagationType;
+
+/**
+ * The base {@link RequestAdapter} for all synchronous HTTP requests.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class HttpRequestAdapter implements RequestAdapter {
+
+ /**
+ * Http server request providing data we need.
+ */
+ private HttpRequest httpRequest;
+
+ /**
+ * Default constructor.
+ *
+ * @param httpRequest
+ * Http server request providing data we need.
+ */
+ public HttpRequestAdapter(HttpRequest httpRequest) {
+ this.httpRequest = httpRequest;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PropagationType getPropagationType() {
+ return PropagationType.HTTP;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Map getTags() {
+ String url = httpRequest.getUrl();
+ String method = httpRequest.getHttpMethod();
+
+ if ((null == url) && (null == method)) {
+ return Collections.emptyMap();
+ }
+
+ Map tags = new HashMap(2, 1f);
+ if (null != url) {
+ tags.put(Tags.HTTP_URL.getKey(), url);
+ }
+ if (null != method) {
+ tags.put(Tags.HTTP_METHOD.getKey(), method);
+ }
+ return tags;
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/HttpResponseAdapter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/HttpResponseAdapter.java
new file mode 100644
index 000000000..44d0f74c0
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/HttpResponseAdapter.java
@@ -0,0 +1,45 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http;
+
+import java.util.Collections;
+import java.util.Map;
+
+import io.opentracing.tag.Tags;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpResponse;
+
+/**
+ * The base {@link ResponseAdapter} for all synchronous HTTP client responses.
+ *
+ * @author Ivan Senic
+ */
+public class HttpResponseAdapter implements ResponseAdapter {
+
+ /**
+ * HTTP response to read data from.
+ */
+ private HttpResponse httpResponse;
+
+ /**
+ * Default constructor.
+ *
+ * @param httpResponse
+ * HTTP response to read data from.
+ */
+ public HttpResponseAdapter(HttpResponse httpResponse) {
+ this.httpResponse = httpResponse;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Map getTags() {
+ int status = httpResponse.getStatus();
+ if (status > 0) {
+ return Collections. singletonMap(Tags.HTTP_STATUS.getKey(), String.valueOf(status));
+ } else {
+ return Collections.emptyMap();
+ }
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/HttpServerRequestAdapter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/HttpServerRequestAdapter.java
new file mode 100644
index 000000000..bbc42d0cb
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/HttpServerRequestAdapter.java
@@ -0,0 +1,47 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http;
+
+import io.opentracing.propagation.Format;
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.tracing.core.adapter.ServerRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpRequest;
+
+/**
+ * The {@link ServerRequestAdapter} for all synchronous HTTP server requests.
+ *
+ * @author Ivan Senic
+ */
+public class HttpServerRequestAdapter extends HttpRequestAdapter implements ServerRequestAdapter {
+
+ /**
+ * Http request providing data we need.
+ */
+ private HttpRequest httpRequest;
+
+ /**
+ * Default constructor.
+ *
+ * @param httpRequest
+ * Http request providing data we need.
+ */
+ public HttpServerRequestAdapter(HttpRequest httpRequest) {
+ super(httpRequest);
+ this.httpRequest = httpRequest;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Format getFormat() {
+ return Format.Builtin.HTTP_HEADERS;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public TextMap getCarrier() {
+ return httpRequest;
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/HttpRequest.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/HttpRequest.java
new file mode 100644
index 000000000..7c6f09e47
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/HttpRequest.java
@@ -0,0 +1,41 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http.data;
+
+import io.opentracing.propagation.TextMap;
+
+/**
+ * The interface to represent HTTP request. This means it needs to provide information about request
+ * as URL and HTTP method, as well as implement the {@link TextMap} in order to propagate/get the
+ * tracing information and baggage with/from the request.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface HttpRequest extends TextMap {
+
+ /**
+ * Defines if the request can be traced at this point of time and new span should be started.
+ * The reasons not to start can be that the trace information is already set, or connection has
+ * already been made.
+ *
+ * If returned true
it's expected that http request does not yet have tracing
+ * information in the headers and that headers can be inserted.
+ *
+ * @return true
if interceptor should start new request, false
+ * otherwise.
+ */
+ boolean startClientSpan();
+
+ /**
+ * Returns URL of the request.
+ *
+ * @return Returns URL of the request.
+ */
+ String getUrl();
+
+ /**
+ * Returns the HTTP method being executed (get, post, etc).
+ *
+ * @return Returns the HTTP method being executed (get, post, etc).
+ */
+ String getHttpMethod();
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/HttpResponse.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/HttpResponse.java
new file mode 100644
index 000000000..b3ca9c89a
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/HttpResponse.java
@@ -0,0 +1,17 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http.data;
+
+/**
+ * The interface to represent HTTP request. Provides only the status of the response.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface HttpResponse {
+
+ /**
+ * Returns the status of the response.
+ *
+ * @return Returns the status of the response.
+ */
+ int getStatus();
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/ApacheHttpClientV40HttpClientRequest.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/ApacheHttpClientV40HttpClientRequest.java
new file mode 100644
index 000000000..ef2570ed5
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/ApacheHttpClientV40HttpClientRequest.java
@@ -0,0 +1,114 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import rocks.inspectit.agent.java.sdk.opentracing.internal.constants.PropagationConstants;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpRequest;
+import rocks.inspectit.agent.java.util.ReflectionCache;
+
+/**
+ * The implementation of the {@link HttpClientRequest} that works with the
+ * {@link org.apache.http.HttpRequest}.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class ApacheHttpClientV40HttpClientRequest implements HttpRequest {
+
+ /**
+ * FQN of the org.apache.http.HttpMessage.
+ */
+ private static final String ORG_APACHE_HTTP_HTTP_MESSAGE_FQN = "org.apache.http.HttpMessage";
+
+ /**
+ * FQN of the org.apache.http.RequestLine.
+ */
+ private static final String ORG_APACHE_HTTP_REQUEST_LINE_FQN = "org.apache.http.RequestLine";
+
+ /**
+ * FQN of the org.apache.http.HttpRequest.
+ */
+ private static final String ORG_APACHE_HTTP_HTTP_REQUEST_FQN = "org.apache.http.HttpRequest";
+
+ /**
+ * Reflection cache to use for method invocation.
+ */
+ private final ReflectionCache cache;
+
+ /**
+ * Apache http request, instance of org.apache.http.HttpRequest.
+ */
+ private final Object apacheHttpRequest;
+
+ /**
+ * @param apacheHttpRequest
+ * Apache http request, instance of org.apache.http.HttpRequest.
+ * @param cache
+ * reflection cache to use
+ */
+ public ApacheHttpClientV40HttpClientRequest(Object apacheHttpRequest, ReflectionCache cache) {
+ this.apacheHttpRequest = apacheHttpRequest;
+ this.cache = cache;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean startClientSpan() {
+ Object containsHeader = cache.invokeMethod(apacheHttpRequest.getClass(), "containsHeader", new Class>[] { String.class }, apacheHttpRequest, new Object[] { PropagationConstants.SPAN_ID }, null, ORG_APACHE_HTTP_HTTP_MESSAGE_FQN);
+ // make sure we return true if the contains key is null
+ return (null == containsHeader) || Boolean.FALSE.equals(containsHeader);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getUrl() {
+ // Apache provides complete URL as the URI (no other option)
+ Object requestLine = getRequestLine();
+ if (null != requestLine) {
+ return (String) cache.invokeMethod(requestLine.getClass(), "getUri", new Class>[] {}, requestLine, new Object[] {}, null, ORG_APACHE_HTTP_REQUEST_LINE_FQN);
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getHttpMethod() {
+ Object requestLine = getRequestLine();
+ if (null != requestLine) {
+ return (String) cache.invokeMethod(requestLine.getClass(), "getMethod", new Class>[] {}, requestLine, new Object[] {}, null, ORG_APACHE_HTTP_REQUEST_LINE_FQN);
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterator> iterator() {
+ throw new UnsupportedOperationException("Client request does not provide baggage iterator.");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void put(String key, String value) {
+ cache.invokeMethod(apacheHttpRequest.getClass(), "setHeader", new Class>[] { String.class, String.class }, apacheHttpRequest, new Object[] { key, value }, null,
+ ORG_APACHE_HTTP_HTTP_MESSAGE_FQN);
+ }
+
+ /**
+ * @return Returns the request line from the request.
+ */
+ private Object getRequestLine() {
+ return cache.invokeMethod(apacheHttpRequest.getClass(), "getRequestLine", new Class>[] {}, apacheHttpRequest, new Object[] {}, null, ORG_APACHE_HTTP_HTTP_REQUEST_FQN);
+ }
+
+}
\ No newline at end of file
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/ApacheHttpClientV40HttpResponse.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/ApacheHttpClientV40HttpResponse.java
new file mode 100644
index 000000000..076bfbeb5
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/ApacheHttpClientV40HttpResponse.java
@@ -0,0 +1,59 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl;
+
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpResponse;
+import rocks.inspectit.agent.java.util.ReflectionCache;
+
+/**
+ * The implementation of the {@link HttpResponse} that works with the
+ * {@link org.apache.http.HttpResponse}.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class ApacheHttpClientV40HttpResponse implements HttpResponse {
+
+ /**
+ * FQN of the org.apache.http.StatusLine.
+ */
+ private static final String ORG_APACHE_HTTP_STATUS_LINE_FQN = "org.apache.http.StatusLine";
+
+ /**
+ * FQN of the org.apache.http.HttpResponse.
+ */
+ private static final String ORG_APACHE_HTTP_HTTP_RESPONSE_FQN = "org.apache.http.HttpResponse";
+
+ /**
+ * Reflection cache to use for method invocation.
+ */
+ private final ReflectionCache cache;
+
+ /**
+ * Apache http response, instance of org.apache.http.HttpResponse.
+ */
+ private final Object apacheHttpResponse;
+
+ /**
+ * @param apacheHttpResponse
+ * Apache http response, instance of org.apache.http.HttpResponse.
+ * @param cache
+ * reflection cache to use
+ */
+ public ApacheHttpClientV40HttpResponse(Object apacheHttpResponse, ReflectionCache cache) {
+ this.apacheHttpResponse = apacheHttpResponse;
+ this.cache = cache;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getStatus() {
+ int result = 0;
+ Object statusLine = cache.invokeMethod(apacheHttpResponse.getClass(), "getStatusLine", new Class>[] {}, apacheHttpResponse, new Object[] {}, null, ORG_APACHE_HTTP_HTTP_RESPONSE_FQN);
+ if (null != statusLine) {
+ result = (Integer) cache.invokeMethod(statusLine.getClass(), "getStatusCode", new Class>[] {}, statusLine, new Object[] {}, Integer.valueOf(0), ORG_APACHE_HTTP_STATUS_LINE_FQN);
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/AsyncHttpResponse.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/AsyncHttpResponse.java
new file mode 100644
index 000000000..2f9af4271
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/AsyncHttpResponse.java
@@ -0,0 +1,34 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl;
+
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpResponse;
+
+/**
+ * Asynchronous HTTP response.
+ *
+ * @author Ivan Senic
+ *
+ */
+public final class AsyncHttpResponse implements HttpResponse {
+
+ /**
+ * Instance for usage.
+ */
+ public static final AsyncHttpResponse INSTANCE = new AsyncHttpResponse();
+
+ /**
+ * Private constructor, no initialization.
+ */
+ private AsyncHttpResponse() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Cannot read response code since is asynchronous connection.
+ */
+ @Override
+ public int getStatus() {
+ return 0;
+ }
+
+}
\ No newline at end of file
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/JavaHttpResponse.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/JavaHttpResponse.java
new file mode 100644
index 000000000..9ffb9c0b9
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/JavaHttpResponse.java
@@ -0,0 +1,51 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl;
+
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpResponse;
+import rocks.inspectit.agent.java.util.ReflectionCache;
+
+/**
+ * Our implementation of the {@link HttpResponse} class working with
+ * {@link javax.servlet.HttpServletResponse}. We only need to extract data from the original
+ * javax.servlet.HttpServletResponse.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class JavaHttpResponse implements HttpResponse {
+
+ /**
+ * FQN of the javax.servlet.HttpServletResponse.
+ */
+ private static final String JAVAX_SERVLET_HTTP_SERVLET_RESPONSE_FQN = "javax.servlet.HttpServletResponse";
+
+ /**
+ * Reflection cache to use for method invocation.
+ */
+ private final ReflectionCache cache;
+
+ /**
+ * Cache for the Method
elements. One {@link ReflectionCache} for all the
+ * instances of this class.
+ */
+ private final Object httpServletResponse;
+
+ /**
+ * @param httpServletResponse
+ * response object
+ * @param cache
+ * reflection cache to use
+ */
+ public JavaHttpResponse(Object httpServletResponse, ReflectionCache cache) {
+ this.httpServletResponse = httpServletResponse;
+ this.cache = cache;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getStatus() {
+ return (Integer) cache.invokeMethod(httpServletResponse.getClass(), "getStatus", new Class>[] {}, httpServletResponse, new Object[] {}, 0, JAVAX_SERVLET_HTTP_SERVLET_RESPONSE_FQN);
+ }
+
+}
\ No newline at end of file
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/JavaHttpServerRequest.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/JavaHttpServerRequest.java
new file mode 100644
index 000000000..003651c52
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/JavaHttpServerRequest.java
@@ -0,0 +1,111 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpRequest;
+import rocks.inspectit.agent.java.util.ReflectionCache;
+
+/**
+ * Our implementation of the {@link HttpServerRequest} that works with
+ * {@link javax.servlet.HttpServletRequest}. We only need to extract data from the original
+ * javax.servlet.HttpServletRequest.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class JavaHttpServerRequest implements HttpRequest {
+
+ /**
+ * FQN constant of the javax.servlet.HttpServletRequest.
+ */
+ private static final String JAVAX_SERVLET_HTTP_SERVLET_REQUEST_CLASS = "javax.servlet.HttpServletRequest";
+
+ /**
+ * Reflection cache to use for method invocation.
+ */
+ private final ReflectionCache cache;
+
+ /**
+ * Object representing http servlet request.
+ */
+ private final Object httpServletRequest;
+
+ /**
+ * @param httpServletRequest
+ * request object
+ * @param cache
+ * reflection cache to use
+ */
+ public JavaHttpServerRequest(Object httpServletRequest, ReflectionCache cache) {
+ this.httpServletRequest = httpServletRequest;
+ this.cache = cache;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean startClientSpan() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getUrl() {
+ Object url = cache.invokeMethod(httpServletRequest.getClass(), "getRequestURL", new Class>[] {}, httpServletRequest, new Object[] {}, null, JAVAX_SERVLET_HTTP_SERVLET_REQUEST_CLASS);
+ if (null != url) {
+ return url.toString();
+ } else {
+ // fail back to URI
+ return (String) cache.invokeMethod(httpServletRequest.getClass(), "getRequestURI", new Class>[] {}, httpServletRequest, new Object[] {}, null, JAVAX_SERVLET_HTTP_SERVLET_REQUEST_CLASS);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getHttpMethod() {
+ return (String) cache.invokeMethod(httpServletRequest.getClass(), "getMethod", new Class>[] {}, httpServletRequest, new Object[] {}, null, JAVAX_SERVLET_HTTP_SERVLET_REQUEST_CLASS);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterator> iterator() {
+ Object headerNames = cache.invokeMethod(httpServletRequest.getClass(), "getHeaderNames", new Class>[] {}, httpServletRequest, new Object[] {}, null,
+ JAVAX_SERVLET_HTTP_SERVLET_REQUEST_CLASS);
+ if (headerNames instanceof Enumeration>) {
+ Enumeration> enumeration = (Enumeration>) headerNames;
+ Map baggage = new HashMap();
+ while (enumeration.hasMoreElements()) {
+ String headerName = enumeration.nextElement().toString();
+ String headerValue = (String) cache.invokeMethod(httpServletRequest.getClass(), "getHeader", new Class>[] { String.class }, httpServletRequest, new Object[] { headerName }, null,
+ JAVAX_SERVLET_HTTP_SERVLET_REQUEST_CLASS);
+ if (null != headerValue) {
+ baggage.put(headerName, headerValue);
+ }
+ }
+ return baggage.entrySet().iterator();
+ } else {
+ return Collections. emptyMap().entrySet().iterator();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void put(String key, String value) {
+ throw new UnsupportedOperationException("Server request does not provide option to put baggage.");
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/JettyHttpClientV61HttpClientRequest.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/JettyHttpClientV61HttpClientRequest.java
new file mode 100644
index 000000000..cb818ae5b
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/JettyHttpClientV61HttpClientRequest.java
@@ -0,0 +1,119 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import org.apache.commons.lang.ArrayUtils;
+
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpRequest;
+import rocks.inspectit.agent.java.util.ReflectionCache;
+
+/**
+ * The {@link HttpClientRequest} implementation that works with Jetty Http client up to version 8.
+ * We expected either {@link org.mortbay.jetty.client.HttpExchange} or
+ * {@link org.eclipse.jetty.client.HttpExchange} as data provider.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class JettyHttpClientV61HttpClientRequest implements HttpRequest {
+
+ /**
+ * Reflection cache to use for method invocation.
+ */
+ private final ReflectionCache cache;
+
+ /**
+ * Jetty http exchange object, instance of org.mortbay.jetty.client.HttpExchange or
+ * org.eclipse.jetty.client.HttpExchange.
+ */
+ private final Object jettyHttpExchange;
+
+ /**
+ * @param jettyHttpExchange
+ * Jetty http exchange object, instance of org.mortbay.jetty.client.HttpExchange or
+ * org.eclipse.jetty.client.HttpExchange.
+ * @param cache
+ * reflection cache to use
+ */
+ public JettyHttpClientV61HttpClientRequest(Object jettyHttpExchange, ReflectionCache cache) {
+ this.jettyHttpExchange = jettyHttpExchange;
+ this.cache = cache;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean startClientSpan() {
+ // http exchange does not provide easy way to find out if our headers have already be
+ // inserted, so we assume it's true always
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getUrl() {
+ // jetty does not provide any method that returns complete URL, thus we need to construct it
+ // on our own completely
+ StringBuilder url = new StringBuilder();
+
+ // first scheme
+ // can not go through interface as we are getting concrete implementation
+ Object scheme = cache.invokeMethod(jettyHttpExchange.getClass(), "getScheme", new Class>[] {}, jettyHttpExchange, new Object[] {}, null);
+ if (null != scheme) {
+ // call to array() to get the bytes, if returned use for constructing string
+ byte[] schemeBytes = (byte[]) cache.invokeMethod(scheme.getClass(), "array", new Class>[] {}, scheme, new Object[] {}, null);
+ if (ArrayUtils.isNotEmpty(schemeBytes)) {
+ url.append(new String(schemeBytes));
+ url.append("://");
+ }
+ }
+
+ // then address
+ Object address = cache.invokeMethod(jettyHttpExchange.getClass(), "getAddress", new Class>[] {}, jettyHttpExchange, new Object[] {}, null);
+ if (null != address) {
+ url.append(address);
+ }
+
+ // then uri
+ Object uri = cache.invokeMethod(jettyHttpExchange.getClass(), "getRequestURI", new Class>[] {}, jettyHttpExchange, new Object[] {}, null);
+ if (null != uri) {
+ url.append(uri);
+ }
+
+ if (url.length() > 0) {
+ return url.toString();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getHttpMethod() {
+ return (String) cache.invokeMethod(jettyHttpExchange.getClass(), "getMethod", new Class>[] {}, jettyHttpExchange, new Object[] {}, null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterator> iterator() {
+ throw new UnsupportedOperationException("Client request does not provide baggage iterator.");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void put(String key, String value) {
+ // can not go through interface as we are getting concrete implementation
+ cache.invokeMethod(jettyHttpExchange.getClass(), "setRequestHeader", new Class>[] { String.class, String.class }, jettyHttpExchange, new Object[] { key, value }, null);
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/SpringRestTemplateHttpClientRequest.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/SpringRestTemplateHttpClientRequest.java
new file mode 100644
index 000000000..cf18d2012
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/SpringRestTemplateHttpClientRequest.java
@@ -0,0 +1,112 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import rocks.inspectit.agent.java.sdk.opentracing.internal.constants.PropagationConstants;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpRequest;
+import rocks.inspectit.agent.java.util.ReflectionCache;
+
+/**
+ * The {@link HttpClientRequest} that works with Spring Rest Template HTTP request. Expects
+ * {@link org.springframework.http.HttpRequest}.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class SpringRestTemplateHttpClientRequest implements HttpRequest {
+
+ /**
+ * FQN of the org.springframework.http.HttpMessage.
+ */
+ private static final String ORG_SPRINGFRAMEWORK_HTTP_HTTP_MESSAGE = "org.springframework.http.HttpMessage";
+
+ /**
+ * FQN of the org.springframework.http.HttpRequest.
+ */
+ private static final String ORG_SPRINGFRAMEWORK_HTTP_HTTP_REQUEST = "org.springframework.http.HttpRequest";
+
+ /**
+ * Reflection cache to use for method invocation.
+ */
+ private final ReflectionCache cache;
+
+ /**
+ * Spring http request, instance of org.springframework.http.HttpRequest.
+ */
+ private final Object springHttpRequest;
+
+ /**
+ * @param cache
+ * Reflection cache to use for method invocation.
+ * @param springHttpRequest
+ * Spring http request, instance of org.springframework.http.HttpRequest.
+ */
+ public SpringRestTemplateHttpClientRequest(Object springHttpRequest, ReflectionCache cache) {
+ this.springHttpRequest = springHttpRequest;
+ this.cache = cache;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean startClientSpan() {
+ Object headers = getHttpHeaders();
+ if (null != headers) {
+ Object containsKey = cache.invokeMethod(headers.getClass(), "containsKey", new Class[] { Object.class }, headers, new Object[] { PropagationConstants.SPAN_ID }, null);
+ // make sure we return true if the contains key is null
+ return (null == containsKey) || Boolean.FALSE.equals(containsKey);
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getUrl() {
+ // String template provides complete URL with the getURI() method
+ Object uri = cache.invokeMethod(springHttpRequest.getClass(), "getURI", new Class[] {}, springHttpRequest, new Object[] {}, null, ORG_SPRINGFRAMEWORK_HTTP_HTTP_REQUEST);
+ if (null != uri) {
+ return uri.toString();
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getHttpMethod() {
+ Object method = cache.invokeMethod(springHttpRequest.getClass(), "getMethod", new Class[] {}, springHttpRequest, new Object[] {}, null, ORG_SPRINGFRAMEWORK_HTTP_HTTP_REQUEST);
+ if (null != method) {
+ return method.toString();
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterator> iterator() {
+ throw new UnsupportedOperationException("Client request does not provide baggage iterator.");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void put(String key, String value) {
+ Object headers = getHttpHeaders();
+ if (null != headers) {
+ cache.invokeMethod(headers.getClass(), "set", new Class[] { String.class, String.class }, headers, new Object[] { key, value }, null);
+ }
+ }
+
+ private Object getHttpHeaders() {
+ return cache.invokeMethod(springHttpRequest.getClass(), "getHeaders", new Class[] {}, springHttpRequest, new Object[] {}, null, ORG_SPRINGFRAMEWORK_HTTP_HTTP_MESSAGE);
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/SpringRestTemplateHttpResponse.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/SpringRestTemplateHttpResponse.java
new file mode 100644
index 000000000..b92971b08
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/SpringRestTemplateHttpResponse.java
@@ -0,0 +1,53 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl;
+
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpResponse;
+import rocks.inspectit.agent.java.util.ReflectionCache;
+
+/**
+ * The {@link HttpResponse} implementation that works with Spring Rest Template HTTP response.
+ * Expects {@link org.springframework.http.client.ClientHttpResponse}.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class SpringRestTemplateHttpResponse implements HttpResponse {
+
+ /**
+ * FQN of the org.springframework.http.client.ClientHttpResponse.
+ */
+ private static final String ORG_SPRINGFRAMEWORK_HTTP_CLIENT_CLIENT_HTTP_RESPONSE = "org.springframework.http.client.ClientHttpResponse";
+
+ /**
+ * Reflection cache to use for method invocation.
+ */
+ private final ReflectionCache cache;
+
+ /**
+ * Spring http response, instance of org.springframework.http.client.ClientHttpResponse.
+ */
+ private final Object springClientHttpResponse;
+
+ /**
+ * Default constructor.
+ *
+ * @param springClientHttpResponse
+ * Spring http response, instance of
+ * org.springframework.http.client.ClientHttpResponse.
+ * @param cache
+ * Reflection cache to use for method invocation.
+ */
+ public SpringRestTemplateHttpResponse(Object springClientHttpResponse, ReflectionCache cache) {
+ this.springClientHttpResponse = springClientHttpResponse;
+ this.cache = cache;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getStatus() {
+ return (Integer) cache.invokeMethod(springClientHttpResponse.getClass(), "getRawStatusCode", new Class[] {}, springClientHttpResponse, new Object[] {}, Integer.valueOf(0),
+ ORG_SPRINGFRAMEWORK_HTTP_CLIENT_CLIENT_HTTP_RESPONSE);
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/UrlConnectionHttpClientRequestResponse.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/UrlConnectionHttpClientRequestResponse.java
new file mode 100644
index 000000000..da11201e7
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/http/data/impl/UrlConnectionHttpClientRequestResponse.java
@@ -0,0 +1,104 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.http.data.impl;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import rocks.inspectit.agent.java.sdk.opentracing.internal.constants.PropagationConstants;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpRequest;
+import rocks.inspectit.agent.java.tracing.core.adapter.http.data.HttpResponse;
+
+/**
+ * The {@link HttpClientRequest} and {@link HttpResponse} implementation that works with
+ * {@link java.net.HttpURLConnection}.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class UrlConnectionHttpClientRequestResponse implements HttpRequest, HttpResponse {
+
+ /**
+ * Http url connection, instance of java.net.HttpURLConnection.
+ */
+ private final HttpURLConnection urlConnection;
+
+ /**
+ * Default constructor.
+ *
+ * @param urlConnection
+ * Http url connection, instance of java.net.HttpURLConnection.
+ */
+ public UrlConnectionHttpClientRequestResponse(HttpURLConnection urlConnection) {
+ this.urlConnection = urlConnection;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean startClientSpan() {
+ // here we do a small trick we try to set the request method to what already is
+ // this will throw an exception if the connection can not be "changed" at this point of time
+ // this way we ensure that no new span will be started if we can not modify the request
+ try {
+ urlConnection.setRequestMethod(urlConnection.getRequestMethod());
+
+ // make sure no inspectit data exists in the request
+ // otherwise request was already correctly populated
+ return null == urlConnection.getRequestProperty(PropagationConstants.SPAN_ID);
+ } catch (Exception e) { // NOPMD
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getUrl() {
+ URL url = urlConnection.getURL();
+ if (null != url) {
+ return url.toString();
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getStatus() {
+ try {
+ return urlConnection.getResponseCode();
+ } catch (IOException e) {
+ return 0;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getHttpMethod() {
+ return urlConnection.getRequestMethod();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterator> iterator() {
+ throw new UnsupportedOperationException("Client request does not provide baggage iterator.");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void put(String key, String value) {
+ urlConnection.setRequestProperty(key, value);
+ }
+
+}
\ No newline at end of file
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/mq/MQRequestAdapter.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/mq/MQRequestAdapter.java
new file mode 100644
index 000000000..872596c08
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/mq/MQRequestAdapter.java
@@ -0,0 +1,101 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.mq;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import io.opentracing.References;
+import io.opentracing.propagation.Format;
+import io.opentracing.propagation.TextMap;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ServerRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.mq.data.MQMessage;
+import rocks.inspectit.shared.all.tracing.constants.ExtraTags;
+import rocks.inspectit.shared.all.tracing.data.PropagationType;
+
+/**
+ * The {@link ServerRequestAdapter} and {@link ClientRequestAdapter} for the MQ.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class MQRequestAdapter implements ServerRequestAdapter, ClientRequestAdapter {
+
+ /**
+ * Message.
+ */
+ private MQMessage message;
+
+ /**
+ * Default constructor.
+ *
+ * @param message
+ * Message
+ */
+ public MQRequestAdapter(MQMessage message) {
+ this.message = message;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean startClientSpan() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PropagationType getPropagationType() {
+ return PropagationType.JMS;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getReferenceType() {
+ return References.FOLLOWS_FROM;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Map getTags() {
+ String messageId = message.getId();
+ String messageDestination = message.getDestination();
+
+ if ((null == messageId) && (null == messageDestination)) {
+ return Collections.emptyMap();
+ }
+
+ Map tags = new HashMap(2, 1f);
+ if (null != messageId) {
+ tags.put(ExtraTags.JMS_MESSAGE_ID, messageId);
+ }
+ if (null != messageDestination) {
+ tags.put(ExtraTags.JMS_MESSAGE_DESTINATION, messageDestination);
+ }
+ return tags;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Format getFormat() {
+ return Format.Builtin.TEXT_MAP;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public TextMap getCarrier() {
+ return message;
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/mq/data/MQMessage.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/mq/data/MQMessage.java
new file mode 100644
index 000000000..9554aa46c
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/mq/data/MQMessage.java
@@ -0,0 +1,29 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.mq.data;
+
+import io.opentracing.propagation.TextMap;
+
+/**
+ * The interface to represent the MQ Message. Should be able to provide the message id and
+ * destination, as well as implement the {@link TextMap} in order to propagate/get the tracing
+ * information and baggage with/from the message.
+ *
+ * @author Ivan Senic
+ *
+ */
+public interface MQMessage extends TextMap {
+
+ /**
+ * Returns the message ID.
+ *
+ * @return Message ID.
+ */
+ String getId();
+
+ /**
+ * Returns the message destination. Destination could be a topic or a queue.
+ *
+ * @return Message destination
+ */
+ String getDestination();
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/mq/data/impl/JmsMessage.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/mq/data/impl/JmsMessage.java
new file mode 100644
index 000000000..109dfd8a5
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/adapter/mq/data/impl/JmsMessage.java
@@ -0,0 +1,101 @@
+package rocks.inspectit.agent.java.tracing.core.adapter.mq.data.impl;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import rocks.inspectit.agent.java.tracing.core.adapter.mq.data.MQMessage;
+import rocks.inspectit.agent.java.util.ReflectionCache;
+
+/**
+ * The implementation of the {@link MQMessage} that works with {@link javax.jms.Message}.
+ *
+ * @author Ivan Senic
+ *
+ */
+public class JmsMessage implements MQMessage {
+
+ /**
+ * FQN of the javax.jms.Message.
+ */
+ public static final String JAVAX_JMS_MESSAGE = "javax.jms.Message";
+
+ /**
+ * Reflection cache to use for method invocation.
+ */
+ private final ReflectionCache cache;
+
+ /**
+ * JMS message, instance of javax.jms.Message.
+ */
+ private final Object message;
+
+ /**
+ * @param message
+ * JMS message, instance of javax.jms.Message.
+ * @param cache
+ * reflection cache to use
+ */
+ public JmsMessage(Object message, ReflectionCache cache) {
+ this.cache = cache;
+ this.message = message;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getId() {
+ return (String) cache.invokeMethod(message.getClass(), "getJMSMessageID", new Class>[] {}, message, new Object[] {}, null, JAVAX_JMS_MESSAGE);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDestination() {
+ Object destination = cache.invokeMethod(message.getClass(), "getJMSDestination", new Class>[] {}, message, new Object[] {}, null, JAVAX_JMS_MESSAGE);
+ if (null != destination) {
+ // we know here it can be queue or topic, not sure how to handle this
+ // for now we just use toString() method
+ return destination.toString();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterator> iterator() {
+ Object properties = cache.invokeMethod(message.getClass(), "getPropertyNames", new Class>[] {}, message, new Object[] {}, null, JAVAX_JMS_MESSAGE);
+ if (properties instanceof Enumeration>) {
+ Enumeration> enumeration = (Enumeration>) properties;
+ Map baggage = new HashMap();
+ while (enumeration.hasMoreElements()) {
+ String propertyName = enumeration.nextElement().toString();
+ String propertyValue = (String) cache.invokeMethod(message.getClass(), "getStringProperty", new Class>[] { String.class }, message, new Object[] { propertyName }, null,
+ JAVAX_JMS_MESSAGE);
+ if (null != propertyValue) {
+ baggage.put(propertyName, propertyValue);
+ }
+ }
+ return baggage.entrySet().iterator();
+ } else {
+ return Collections. emptyMap().entrySet().iterator();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void put(String key, String value) {
+ cache.invokeMethod(message.getClass(), "setStringProperty", new Class>[] { String.class, String.class }, message, new Object[] { key, value }, null, JAVAX_JMS_MESSAGE);
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/transformer/SpanContextTransformer.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/transformer/SpanContextTransformer.java
new file mode 100644
index 000000000..4c53e769d
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/transformer/SpanContextTransformer.java
@@ -0,0 +1,54 @@
+package rocks.inspectit.agent.java.tracing.core.transformer;
+
+import org.apache.commons.collections.Transformer;
+
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanContextImpl;
+import rocks.inspectit.shared.all.tracing.data.SpanIdent;
+
+/**
+ * Span context transformer knows to to translate opentracing.io based {@link SpanContextImpl} to
+ * the {@link SpanIdent}.
+ *
+ * @author Ivan Senic
+ *
+ */
+public final class SpanContextTransformer implements Transformer {
+
+ /**
+ * Instance for usage.
+ */
+ public static final SpanContextTransformer INSTANCE = new SpanContextTransformer();
+
+ /**
+ * Private, use {@link #INSTANCE} or {@link #transformSpanContext(SpanContextImpl)}.
+ */
+ private SpanContextTransformer() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SpanIdent transform(Object input) {
+ if (input instanceof SpanContextImpl) {
+ return transformSpanContext((SpanContextImpl) input);
+ }
+ throw new IllegalArgumentException("Can not transform the instance of " + input.getClass());
+ }
+
+ /**
+ * Transforms the {@link SpanContextImpl} to the {@link SpanIdent}.
+ *
+ * @param context
+ * context.
+ * @return {@link SpanIdent}.
+ */
+ public static SpanIdent transformSpanContext(SpanContextImpl context) {
+ if (null == context) {
+ return null;
+ }
+
+ return new SpanIdent(context.getId(), context.getTraceId(), context.getParentId());
+ }
+
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/transformer/SpanTransformer.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/transformer/SpanTransformer.java
new file mode 100644
index 000000000..7fefbf329
--- /dev/null
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/tracing/core/transformer/SpanTransformer.java
@@ -0,0 +1,135 @@
+package rocks.inspectit.agent.java.tracing.core.transformer;
+
+import java.sql.Timestamp;
+import java.util.Map;
+
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.collections.Transformer;
+
+import io.opentracing.tag.Tags;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanImpl;
+import rocks.inspectit.shared.all.tracing.constants.ExtraTags;
+import rocks.inspectit.shared.all.tracing.data.AbstractSpan;
+import rocks.inspectit.shared.all.tracing.data.ClientSpan;
+import rocks.inspectit.shared.all.tracing.data.PropagationType;
+import rocks.inspectit.shared.all.tracing.data.ServerSpan;
+import rocks.inspectit.shared.all.tracing.data.SpanIdent;
+
+/**
+ * Span transformer knows to to translate opentracing.io based {@link SpanImpl} to the
+ * {@link AbstractSpan}.
+ *
+ * @author Ivan Senic
+ *
+ */
+public final class SpanTransformer implements Transformer {
+
+ /**
+ * Instance for usage.
+ */
+ public static final SpanTransformer INSTANCE = new SpanTransformer();
+
+ /**
+ * Private, use {@link #INSTANCE} or {@link #transformSpan(SpanImpl)}.
+ */
+ private SpanTransformer() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AbstractSpan transform(Object input) {
+ if (input instanceof SpanImpl) {
+ return transformSpan((SpanImpl) input);
+ }
+ throw new IllegalArgumentException("Can not transform the instance of " + input.getClass());
+ }
+
+ /**
+ * Transforms the opentracing.io span to our internal representation.
+ *
+ * @param spanImpl
+ * {@link SpanImpl}.
+ * @return {@link AbstractSpan}.
+ */
+ public static AbstractSpan transformSpan(SpanImpl spanImpl) {
+ // check not null
+ if (null == spanImpl) {
+ return null;
+ }
+
+ // context to ident
+ SpanIdent ident = SpanContextTransformer.transformSpanContext(spanImpl.context());
+ if (null == ident) {
+ // no sense in creating span without ident
+ return null;
+ }
+
+ AbstractSpan span = createCorrectSpanType(spanImpl);
+ span.setSpanIdent(ident);
+
+ // transform to inspectIT way of time handling
+ long timestampMillis = spanImpl.getStartTimeMicros() / 1000;
+ double durationMillis = spanImpl.getDuration() / 1000.0d;
+ span.setTimeStamp(new Timestamp(timestampMillis));
+ span.setDuration(durationMillis);
+
+ // reference
+ span.setReferenceType(spanImpl.context().getReferenceType());
+
+ // operation name (we save as tag)
+ if (null != spanImpl.getOperationName()) {
+ span.addTag(ExtraTags.OPERATION_NAME, spanImpl.getOperationName());
+ }
+
+ // tags
+ if (MapUtils.isNotEmpty(spanImpl.getTags())) {
+ // extra for propagation
+ String propagation = spanImpl.getTags().get(ExtraTags.PROPAGATION_TYPE);
+ span.setPropagationType(PropagationType.safeValueOf(propagation));
+
+
+ for (Map.Entry entry : spanImpl.getTags().entrySet()) {
+ if (!isTagIgnored(entry.getKey())) {
+ span.addTag(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ // TODO what do we do about log data
+ // we could add that to the ParameterContentData
+
+ return span;
+ }
+
+ /**
+ * Creates {@link ClientSpan} or {@link ServerSpan} based on the information from the given
+ * {@link SpanImpl}.
+ *
+ * @param spanImpl
+ * opentracing span impl
+ * @return {@link AbstractSpan}
+ */
+ private static AbstractSpan createCorrectSpanType(SpanImpl spanImpl) {
+ if (spanImpl.isClient()) {
+ ClientSpan clientSpan = new ClientSpan();
+ return clientSpan;
+ } else {
+ ServerSpan serverSpan = new ServerSpan();
+ return serverSpan;
+ }
+ }
+
+ /**
+ * If tags key is ignored for the copy to the our span representation. Ignored are: propagation
+ * type and spin kind.
+ *
+ * @param key
+ * tag key
+ * @return if tag with given key should be ignored when copied.
+ */
+ private static boolean isTagIgnored(String key) {
+ return ExtraTags.PROPAGATION_TYPE.equals(key) || Tags.SPAN_KIND.getKey().equals(key);
+ }
+}
diff --git a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/util/ClassLoadingUtil.java b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/util/ClassLoadingUtil.java
index e84ffb6c9..1c08d70bf 100644
--- a/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/util/ClassLoadingUtil.java
+++ b/inspectit.agent.java/src/main/java/rocks/inspectit/agent/java/util/ClassLoadingUtil.java
@@ -13,6 +13,11 @@ public final class ClassLoadingUtil {
*/
private static final String CLASS_NAME_PREFIX = "rocks.inspectit";
+ /**
+ * Open tracing package prefix.
+ */
+ private static final String IO_OPENTRACING_PREFIX = "io.opentracing";
+
/**
* Private constructor.
*/
@@ -29,4 +34,16 @@ private ClassLoadingUtil() {
public static boolean isInspectITClass(String className) {
return className.startsWith(CLASS_NAME_PREFIX);
}
+
+ /**
+ * Returns if the class name starts with {@value #IO_OPENTRACING_PREFIX}.
+ *
+ * @param className
+ * Class name
+ * @return Returns if the class name starts with {@value #IO_OPENTRACING_PREFIX}.
+ */
+ public static boolean isOpenTracingClass(String className) {
+ return className.startsWith(IO_OPENTRACING_PREFIX);
+ }
+
}
diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/invocationsequence/InvocationSequenceHookTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/invocationsequence/InvocationSequenceHookTest.java
index e763bc99a..c1cedad11 100644
--- a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/invocationsequence/InvocationSequenceHookTest.java
+++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/invocationsequence/InvocationSequenceHookTest.java
@@ -4,10 +4,12 @@
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -29,6 +31,8 @@
import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
import rocks.inspectit.agent.java.core.ICoreService;
import rocks.inspectit.agent.java.core.IPlatformManager;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanContextImpl;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.TracerImpl;
import rocks.inspectit.agent.java.sensor.ISensor;
import rocks.inspectit.agent.java.sensor.exception.ExceptionSensor;
import rocks.inspectit.agent.java.sensor.method.IMethodSensor;
@@ -36,6 +40,7 @@
import rocks.inspectit.agent.java.sensor.method.jdbc.PreparedStatementParameterSensor;
import rocks.inspectit.agent.java.sensor.method.jdbc.PreparedStatementSensor;
import rocks.inspectit.agent.java.sensor.method.logging.Log4JLoggingSensor;
+import rocks.inspectit.agent.java.tracing.core.transformer.SpanContextTransformer;
import rocks.inspectit.agent.java.util.Timer;
import rocks.inspectit.shared.all.communication.data.ExceptionSensorData;
import rocks.inspectit.shared.all.communication.data.InvocationSequenceData;
@@ -44,6 +49,8 @@
import rocks.inspectit.shared.all.communication.data.TimerData;
import rocks.inspectit.shared.all.instrumentation.config.impl.MethodSensorTypeConfig;
import rocks.inspectit.shared.all.testbase.TestBase;
+import rocks.inspectit.shared.all.tracing.data.ClientSpan;
+import rocks.inspectit.shared.all.tracing.data.SpanIdent;
/**
* Testing the {@link InvocationSequenceHook}.
@@ -65,6 +72,12 @@ public class InvocationSequenceHookTest extends TestBase {
@Mock
private IPlatformManager platformManager;
+ @Mock
+ private ICoreService realCoreService;
+
+ @Mock
+ private TracerImpl tracer;
+
@Mock
private IPropertyAccessor propertyAccessor;
@@ -85,7 +98,7 @@ public class InvocationSequenceHookTest extends TestBase {
@BeforeMethod
public void init() {
- invocationSequenceHook = new InvocationSequenceHook(timer, platformManager, propertyAccessor, Collections. emptyMap(), false);
+ invocationSequenceHook = new InvocationSequenceHook(timer, platformManager, realCoreService, tracer, propertyAccessor, Collections. emptyMap(), false);
}
/**
@@ -133,6 +146,103 @@ public void startEndInvocationWithDataSaving() {
assertThat(invocation.getChildCount(), is(0L));
assertThat(invocation.getTimerData(), is(timerData));
assertThat(invocation.getSqlStatementData(), is(sqlStatementData));
+ assertThat(invocation.getSpanIdent(), is(nullValue()));
+
+ verifyZeroInteractions(realCoreService);
+ }
+
+ /**
+ * Tests that the correct time and ids will be set on the invocation.
+ *
+ * @throws IdNotAvailableException
+ */
+ @Test
+ public void startEndInvocationWithSpanSaving() {
+ long platformId = 1L;
+ long methodId = 3L;
+ long sensorTypeId = 11L;
+ Object object = mock(Object.class);
+ Object[] parameters = new Object[0];
+ Object result = mock(Object.class);
+
+ when(platformManager.getPlatformId()).thenReturn(platformId);
+
+ double firstTimerValue = 1000.0d;
+ double secondTimerValue = 1323.0d;
+ when(timer.getCurrentTime()).thenReturn(firstTimerValue, secondTimerValue);
+ when(rsc.getMethodSensors()).thenReturn(Collections.singletonList(methodSensor));
+ when(methodSensor.getSensorTypeConfig()).thenReturn(methodSensorTypeConfig);
+
+ invocationSequenceHook.beforeBody(methodId, sensorTypeId, object, parameters, rsc);
+ // save span
+ SpanIdent spanIdent = new SpanIdent(0, 0, 0);
+ ClientSpan clientSpan = new ClientSpan();
+ clientSpan.setSpanIdent(spanIdent);
+ invocationSequenceHook.addMethodSensorData(0, 0, "", clientSpan);
+ invocationSequenceHook.firstAfterBody(methodId, sensorTypeId, object, parameters, result, rsc);
+ invocationSequenceHook.secondAfterBody(coreService, methodId, sensorTypeId, object, parameters, result, rsc);
+
+ verify(timer, times(2)).getCurrentTime();
+ verify(realCoreService, times(1)).addMethodSensorData(0, 0, "", clientSpan);
+ verifyNoMoreInteractions(realCoreService);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(InvocationSequenceData.class);
+ verify(coreService, times(1)).addMethodSensorData(eq(sensorTypeId), eq(methodId), Matchers. anyObject(), captor.capture());
+
+ InvocationSequenceData invocation = captor.getValue();
+ assertThat(invocation.getPlatformIdent(), is(platformId));
+ assertThat(invocation.getMethodIdent(), is(methodId));
+ assertThat(invocation.getSensorTypeIdent(), is(sensorTypeId));
+ assertThat(invocation.getDuration(), is(secondTimerValue - firstTimerValue));
+ assertThat(invocation.getNestedSequences(), is(empty()));
+ assertThat(invocation.getChildCount(), is(0L));
+ assertThat(invocation.getSpanIdent(), is(spanIdent));
+
+ }
+
+ /**
+ * Tests that the correct time and ids will be set on the invocation.
+ *
+ * @throws IdNotAvailableException
+ */
+ @Test
+ public void startEndInvocationWithActiveServerSpan() {
+ long platformId = 1L;
+ long methodId = 3L;
+ long sensorTypeId = 11L;
+ Object object = mock(Object.class);
+ Object[] parameters = new Object[0];
+ Object result = mock(Object.class);
+
+ SpanContextImpl context = SpanContextImpl.build();
+ when(tracer.getCurrentContext()).thenReturn(context);
+ when(tracer.isCurrentContextExisting()).thenReturn(true);
+ when(platformManager.getPlatformId()).thenReturn(platformId);
+
+ double firstTimerValue = 1000.0d;
+ double secondTimerValue = 1323.0d;
+ when(timer.getCurrentTime()).thenReturn(firstTimerValue, secondTimerValue);
+ when(rsc.getMethodSensors()).thenReturn(Collections.singletonList(methodSensor));
+ when(methodSensor.getSensorTypeConfig()).thenReturn(methodSensorTypeConfig);
+
+ invocationSequenceHook.beforeBody(methodId, sensorTypeId, object, parameters, rsc);
+ invocationSequenceHook.firstAfterBody(methodId, sensorTypeId, object, parameters, result, rsc);
+ invocationSequenceHook.secondAfterBody(coreService, methodId, sensorTypeId, object, parameters, result, rsc);
+
+ verify(timer, times(2)).getCurrentTime();
+ ArgumentCaptor captor = ArgumentCaptor.forClass(InvocationSequenceData.class);
+ verify(coreService, times(1)).addMethodSensorData(eq(sensorTypeId), eq(methodId), Matchers. anyObject(), captor.capture());
+
+ InvocationSequenceData invocation = captor.getValue();
+ assertThat(invocation.getPlatformIdent(), is(platformId));
+ assertThat(invocation.getMethodIdent(), is(methodId));
+ assertThat(invocation.getSensorTypeIdent(), is(sensorTypeId));
+ assertThat(invocation.getDuration(), is(secondTimerValue - firstTimerValue));
+ assertThat(invocation.getNestedSequences(), is(empty()));
+ assertThat(invocation.getChildCount(), is(0L));
+ assertThat(invocation.getSpanIdent(), is(SpanContextTransformer.transformSpanContext(context)));
+
+ verifyZeroInteractions(realCoreService);
}
/**
@@ -186,6 +296,8 @@ public void twoInvocationsParentChild() {
assertThat(child.getNestedSequences(), is(empty()));
assertThat(child.getParentSequence(), is(invocation));
assertThat(child.getChildCount(), is(0L));
+
+ verifyZeroInteractions(realCoreService);
}
/**
@@ -240,6 +352,8 @@ public void twoRecursiveInvocations() {
assertThat(child.getNestedSequences(), is(empty()));
assertThat(child.getParentSequence(), is(invocation));
assertThat(child.getChildCount(), is(0L));
+
+ verifyZeroInteractions(realCoreService);
}
/**
@@ -285,6 +399,8 @@ public void minDuration() {
verify(timer, times(4)).getCurrentTime();
verify(coreService, times(1)).addMethodSensorData(eq(sensorTypeId), eq(methodId), Matchers. anyObject(), Matchers. anyObject());
+
+ verifyZeroInteractions(realCoreService);
}
/**
@@ -347,6 +463,8 @@ public void fixChildrenOnRemoval() {
assertThat(child.getNestedSequences(), is(empty()));
assertThat(child.getParentSequence(), is(invocation));
assertThat(child.getChildCount(), is(0L));
+
+ verifyZeroInteractions(realCoreService);
}
/**
@@ -395,6 +513,8 @@ public void removeExceptionDelegation() {
assertThat(invocation.getDuration(), is(thirdTimerValue - firstTimerValue));
assertThat(invocation.getNestedSequences(), hasSize(0));
assertThat(invocation.getChildCount(), is(0L));
+
+ verifyZeroInteractions(realCoreService);
}
/**
@@ -457,6 +577,8 @@ public void noRemoveExceptionDelegation() {
assertThat(child.getParentSequence(), is(invocation));
assertThat(child.getChildCount(), is(0L));
assertThat(child.getExceptionSensorDataObjects(), is(Collections.singletonList(exceptionData)));
+
+ verifyZeroInteractions(realCoreService);
}
/**
@@ -505,6 +627,8 @@ public void removeWrappedSql() {
assertThat(invocation.getDuration(), is(thirdTimerValue - firstTimerValue));
assertThat(invocation.getNestedSequences(), hasSize(0));
assertThat(invocation.getChildCount(), is(0L));
+
+ verifyZeroInteractions(realCoreService);
}
/**
@@ -568,6 +692,8 @@ public void noRemoveWrappedSql() {
assertThat(child.getParentSequence(), is(invocation));
assertThat(child.getChildCount(), is(0L));
assertThat(child.getSqlStatementData(), is(sqlStatementData));
+
+ verifyZeroInteractions(realCoreService);
}
/**
@@ -616,6 +742,8 @@ public void removeNotCapturedLogging() {
assertThat(invocation.getDuration(), is(thirdTimerValue - firstTimerValue));
assertThat(invocation.getNestedSequences(), hasSize(0));
assertThat(invocation.getChildCount(), is(0L));
+
+ verifyZeroInteractions(realCoreService);
}
/**
@@ -678,6 +806,8 @@ public void noRemoveCapturedLogging() {
assertThat(child.getParentSequence(), is(invocation));
assertThat(child.getChildCount(), is(0L));
assertThat(child.getLoggingData(), is(loggingData));
+
+ verifyZeroInteractions(realCoreService);
}
/**
@@ -703,7 +833,7 @@ public void skipSingleSensor(Class extends ISensor> sensorClass) {
invocationSequenceHook.firstAfterBody(methodId, sensorTypeId, object, parameters, result, rsc);
invocationSequenceHook.secondAfterBody(coreService, methodId, sensorTypeId, object, parameters, result, rsc);
- verifyZeroInteractions(timer, coreService);
+ verifyZeroInteractions(timer, coreService, realCoreService);
}
/**
@@ -714,7 +844,7 @@ public void skipSingleSensor(Class extends ISensor> sensorClass) {
*/
@Test(dataProvider = "skippingSensors")
public void skipSensorWithEnchancedExceptionSensor(Class extends ISensor> sensorClass) {
- invocationSequenceHook = new InvocationSequenceHook(timer, platformManager, propertyAccessor, Collections. emptyMap(), true);
+ invocationSequenceHook = new InvocationSequenceHook(timer, platformManager, realCoreService, tracer, propertyAccessor, Collections. emptyMap(), true);
long methodId = 3L;
long sensorTypeId = 11L;
@@ -742,7 +872,7 @@ public void skipSensorWithEnchancedExceptionSensor(Class extends ISensor> sens
invocationSequenceHook.firstAfterBody(methodId, sensorTypeId, object, parameters, result, rsc);
invocationSequenceHook.secondAfterBody(coreService, methodId, sensorTypeId, object, parameters, result, rsc);
- verifyZeroInteractions(timer, coreService);
+ verifyZeroInteractions(timer, coreService, realCoreService);
}
@DataProvider(name = "skippingSensors")
diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/RemoteClientHookTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/RemoteClientHookTest.java
new file mode 100644
index 000000000..1b9670de7
--- /dev/null
+++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/RemoteClientHookTest.java
@@ -0,0 +1,289 @@
+package rocks.inspectit.agent.java.sensor.method.remote.client;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.testng.annotations.Test;
+
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+import rocks.inspectit.agent.java.core.ICoreService;
+import rocks.inspectit.agent.java.core.IPlatformManager;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanContextImpl;
+import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanImpl;
+import rocks.inspectit.agent.java.tracing.core.ClientInterceptor;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientAdapterProvider;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.shared.all.testbase.TestBase;
+import rocks.inspectit.shared.all.tracing.data.ClientSpan;
+
+/**
+ * @author Ivan Senic
+ *
+ */
+@SuppressWarnings("PMD")
+public class RemoteClientHookTest extends TestBase {
+
+ @InjectMocks
+ RemoteClientHook hook;
+
+ @Mock
+ ICoreService coreService;
+
+ @Mock
+ ClientInterceptor clientInterceptor;
+
+ @Mock
+ ClientAdapterProvider adapterProvider;
+
+ @Mock
+ ClientRequestAdapter> requestAdapter;
+
+ @Mock
+ ResponseAdapter responseAdapter;
+
+ @Mock
+ IPlatformManager platformManager;
+
+ @Mock
+ Object object;
+
+ @Mock
+ Object result;
+
+ @Mock
+ RegisteredSensorConfig rsc;
+
+ @Test
+ public void happyPath() throws Exception {
+ // ids
+ long platformId = 1l;
+ long methodId = 7l;
+ long sensorId = 13l;
+ long spanId = 17l;
+ // platform
+ when(platformManager.getPlatformId()).thenReturn(platformId);
+ // interceptor
+ Object[] parameters = new String[] { "blah", "bla" };
+ doReturn(requestAdapter).when(adapterProvider).getClientRequestAdapter(object, parameters, rsc);
+ doReturn(responseAdapter).when(adapterProvider).getClientResponseAdapter(object, parameters, result, rsc);
+
+ SpanContextImpl context = mock(SpanContextImpl.class);
+ when(context.getId()).thenReturn(spanId);
+ SpanImpl spanImpl = mock(SpanImpl.class);
+ when(spanImpl.context()).thenReturn(context);
+ when(spanImpl.isClient()).thenReturn(true);
+ when(clientInterceptor.handleRequest(requestAdapter)).thenReturn(spanImpl);
+ when(clientInterceptor.handleResponse(spanImpl, responseAdapter)).thenReturn(spanImpl);
+
+ // execute calls
+ hook.beforeBody(methodId, sensorId, object, parameters, rsc);
+ hook.firstAfterBody(methodId, sensorId, object, parameters, result, rsc);
+ hook.secondAfterBody(coreService, methodId, sensorId, object, parameters, result, rsc);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ClientSpan.class);
+ verify(coreService).addMethodSensorData(eq(sensorId), eq(methodId), eq(String.valueOf(spanId)), captor.capture());
+ assertThat(captor.getValue().getPlatformIdent(), is(platformId));
+ assertThat(captor.getValue().getSensorTypeIdent(), is(sensorId));
+ assertThat(captor.getValue().getMethodIdent(), is(methodId));
+
+ // verify timer, interceptor and adapters
+ verify(clientInterceptor).handleRequest(requestAdapter);
+ verify(clientInterceptor).handleResponse(spanImpl, responseAdapter);
+ verify(adapterProvider).getClientRequestAdapter(object, parameters, rsc);
+ verify(adapterProvider).getClientResponseAdapter(object, parameters, result, rsc);
+ verifyNoMoreInteractions(adapterProvider, clientInterceptor, coreService);
+ }
+
+ @Test
+ public void spanNull() throws Exception {
+ // ids
+ long platformId = 1l;
+ long methodId = 7l;
+ long sensorId = 13l;
+ // platform
+ when(platformManager.getPlatformId()).thenReturn(platformId);
+ // interceptor
+ Object[] parameters = new String[] { "blah", "bla" };
+ doReturn(requestAdapter).when(adapterProvider).getClientRequestAdapter(object, parameters, rsc);
+ doReturn(responseAdapter).when(adapterProvider).getClientResponseAdapter(object, parameters, result, rsc);
+ when(clientInterceptor.handleRequest(requestAdapter)).thenReturn(null);
+
+ // execute calls
+ hook.beforeBody(methodId, sensorId, object, parameters, rsc);
+ hook.firstAfterBody(methodId, sensorId, object, parameters, result, rsc);
+ hook.secondAfterBody(coreService, methodId, sensorId, object, parameters, result, rsc);
+
+ // verify timer, interceptor and adapters
+ verify(clientInterceptor).handleRequest(requestAdapter);
+ verify(adapterProvider).getClientRequestAdapter(object, parameters, rsc);
+ verifyNoMoreInteractions(adapterProvider, clientInterceptor, coreService);
+ verifyZeroInteractions(coreService);
+ }
+
+ @Test
+ public void requestAdapterNull() throws Exception {
+ // ids
+ long platformId = 1l;
+ long methodId = 7l;
+ long sensorId = 13l;
+ // platform
+ when(platformManager.getPlatformId()).thenReturn(platformId);
+ // interceptor
+ Object[] parameters = new String[] { "blah", "bla" };
+ doReturn(null).when(adapterProvider).getClientRequestAdapter(object, parameters, rsc);
+ doReturn(responseAdapter).when(adapterProvider).getClientResponseAdapter(object, parameters, result, rsc);
+
+ // execute calls
+ hook.beforeBody(methodId, sensorId, object, parameters, rsc);
+ hook.firstAfterBody(methodId, sensorId, object, parameters, result, rsc);
+ hook.secondAfterBody(coreService, methodId, sensorId, object, parameters, result, rsc);
+
+ // verify timer, interceptor and adapters
+ verify(adapterProvider).getClientRequestAdapter(object, parameters, rsc);
+ verifyNoMoreInteractions(adapterProvider, clientInterceptor, coreService);
+ verifyZeroInteractions(coreService);
+ }
+
+ @Test
+ public void responseAdapterNull() throws Exception {
+ // ids
+ long platformId = 1l;
+ long methodId = 7l;
+ long sensorId = 13l;
+ long spanId = 17l;
+ // platform
+ when(platformManager.getPlatformId()).thenReturn(platformId);
+ // interceptor
+ Object[] parameters = new String[] { "blah", "bla" };
+ doReturn(requestAdapter).when(adapterProvider).getClientRequestAdapter(object, parameters, rsc);
+ doReturn(null).when(adapterProvider).getClientResponseAdapter(object, parameters, result, rsc);
+
+ SpanContextImpl context = mock(SpanContextImpl.class);
+ when(context.getId()).thenReturn(spanId);
+ SpanImpl spanImpl = mock(SpanImpl.class);
+ when(spanImpl.context()).thenReturn(context);
+ when(spanImpl.isClient()).thenReturn(true);
+ when(clientInterceptor.handleRequest(requestAdapter)).thenReturn(spanImpl);
+
+ // execute calls
+ hook.beforeBody(methodId, sensorId, object, parameters, rsc);
+ hook.firstAfterBody(methodId, sensorId, object, parameters, result, rsc);
+ hook.secondAfterBody(coreService, methodId, sensorId, object, parameters, result, rsc);
+
+ // verify timer, interceptor and adapters
+ verify(clientInterceptor).handleRequest(requestAdapter);
+ verify(adapterProvider).getClientRequestAdapter(object, parameters, rsc);
+ verify(adapterProvider).getClientResponseAdapter(object, parameters, result, rsc);
+ verifyNoMoreInteractions(adapterProvider, clientInterceptor);
+ verifyZeroInteractions(coreService);
+ }
+
+ @Test
+ public void twoCalls() throws Exception {
+ // ids
+ long platformId = 1l;
+ long methodId = 7l;
+ long sensorId = 13l;
+ long spanId = 17l;
+ // platform
+ when(platformManager.getPlatformId()).thenReturn(platformId);
+ // interceptor
+ Object[] parameters = new String[] { "blah", "bla" };
+ doReturn(requestAdapter).when(adapterProvider).getClientRequestAdapter(object, parameters, rsc);
+ doReturn(responseAdapter).when(adapterProvider).getClientResponseAdapter(object, parameters, result, rsc);
+ SpanContextImpl context = mock(SpanContextImpl.class);
+ when(context.getId()).thenReturn(spanId);
+ SpanImpl spanImpl = mock(SpanImpl.class);
+ when(spanImpl.context()).thenReturn(context);
+ when(spanImpl.isClient()).thenReturn(true);
+ when(clientInterceptor.handleRequest(requestAdapter)).thenReturn(spanImpl);
+ when(clientInterceptor.handleResponse(spanImpl, responseAdapter)).thenReturn(spanImpl);
+
+ // execute calls
+ hook.beforeBody(methodId, sensorId, object, parameters, rsc);
+ // new call
+ hook.beforeBody(methodId, sensorId, object, parameters, rsc);
+ hook.firstAfterBody(methodId, sensorId, object, parameters, result, rsc);
+ hook.secondAfterBody(coreService, methodId, sensorId, object, parameters, result, rsc);
+ // end new call
+ hook.firstAfterBody(methodId, sensorId, object, parameters, result, rsc);
+ hook.secondAfterBody(coreService, methodId, sensorId, object, parameters, result, rsc);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ClientSpan.class);
+ verify(coreService).addMethodSensorData(eq(sensorId), eq(methodId), eq(String.valueOf(spanId)), captor.capture());
+ assertThat(captor.getValue().getPlatformIdent(), is(platformId));
+ assertThat(captor.getValue().getSensorTypeIdent(), is(sensorId));
+ assertThat(captor.getValue().getMethodIdent(), is(methodId));
+
+ // verify timer, interceptor and adapters
+ verify(clientInterceptor).handleRequest(requestAdapter);
+ verify(clientInterceptor).handleResponse(spanImpl, responseAdapter);
+ verify(adapterProvider).getClientRequestAdapter(object, parameters, rsc);
+ verify(adapterProvider).getClientResponseAdapter(object, parameters, result, rsc);
+ verifyNoMoreInteractions(adapterProvider, clientInterceptor, coreService);
+ }
+
+
+ @Test
+ public void requestAndResponseInDifferentMethods() throws Exception {
+ // ids
+ long platformId = 1l;
+ long methodId = 7l;
+ long sensorId = 13l;
+ long spanId = 17l;
+ // platform
+ when(platformManager.getPlatformId()).thenReturn(platformId);
+ // interceptor
+ Object[] parameters = new String[] { "blah", "bla" };
+ doReturn(requestAdapter).when(adapterProvider).getClientRequestAdapter(object, parameters, rsc);
+ doReturn(null).when(adapterProvider).getClientResponseAdapter(object, parameters, result, rsc);
+
+ SpanContextImpl context = mock(SpanContextImpl.class);
+ when(context.getId()).thenReturn(spanId);
+ SpanImpl spanImpl = mock(SpanImpl.class);
+ when(spanImpl.context()).thenReturn(context);
+ when(spanImpl.isClient()).thenReturn(true);
+ when(clientInterceptor.handleRequest(requestAdapter)).thenReturn(spanImpl);
+
+ // execute first set of calls
+ hook.beforeBody(methodId, sensorId, object, parameters, rsc);
+ hook.firstAfterBody(methodId, sensorId, object, parameters, result, rsc);
+ hook.secondAfterBody(coreService, methodId, sensorId, object, parameters, result, rsc);
+
+ doReturn(null).when(adapterProvider).getClientRequestAdapter(object, parameters, rsc);
+ doReturn(responseAdapter).when(adapterProvider).getClientResponseAdapter(object, parameters, result, rsc);
+ when(clientInterceptor.handleResponse(spanImpl, responseAdapter)).thenReturn(spanImpl);
+
+ // execute second set of calls
+ hook.beforeBody(methodId, sensorId, object, parameters, rsc);
+ hook.firstAfterBody(methodId, sensorId, object, parameters, result, rsc);
+ hook.secondAfterBody(coreService, methodId, sensorId, object, parameters, result, rsc);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ClientSpan.class);
+ verify(coreService).addMethodSensorData(eq(sensorId), eq(methodId), eq(String.valueOf(spanId)), captor.capture());
+ assertThat(captor.getValue().getPlatformIdent(), is(platformId));
+ assertThat(captor.getValue().getSensorTypeIdent(), is(sensorId));
+ assertThat(captor.getValue().getMethodIdent(), is(methodId));
+
+ // verify timer, interceptor and adapters
+ verify(clientInterceptor).handleRequest(requestAdapter);
+ verify(clientInterceptor).handleResponse(spanImpl, responseAdapter);
+ verify(adapterProvider, times(2)).getClientRequestAdapter(object, parameters, rsc);
+ verify(adapterProvider, times(2)).getClientResponseAdapter(object, parameters, result, rsc);
+ verifyNoMoreInteractions(adapterProvider, clientInterceptor, coreService);
+ }
+
+
+}
diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/ApacheHttpClientV40SensorTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/ApacheHttpClientV40SensorTest.java
new file mode 100644
index 000000000..bbb2ba211
--- /dev/null
+++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/ApacheHttpClientV40SensorTest.java
@@ -0,0 +1,202 @@
+package rocks.inspectit.agent.java.sensor.method.remote.client.http;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.Map;
+
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import io.opentracing.References;
+import io.opentracing.propagation.Format;
+import io.opentracing.propagation.TextMap;
+import io.opentracing.tag.Tags;
+import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
+import rocks.inspectit.agent.java.tracing.core.adapter.ClientRequestAdapter;
+import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter;
+import rocks.inspectit.shared.all.testbase.TestBase;
+import rocks.inspectit.shared.all.tracing.data.PropagationType;
+
+/**
+ * @author Ivan Senic
+ *
+ */
+@SuppressWarnings("PMD")
+public class ApacheHttpClientV40SensorTest extends TestBase {
+
+ @InjectMocks
+ ApacheHttpClientV40Sensor sensor;
+
+ @Mock
+ RegisteredSensorConfig rsc;
+
+ public static class GetClientRequestAdapter extends ApacheHttpClientV40SensorTest {
+
+ @Mock
+ Object object;
+
+ @Mock
+ HttpRequest httpRequest;
+
+ @Mock
+ RequestLine requestLine;
+
+ @BeforeMethod
+ public void setup() {
+ when(httpRequest.getRequestLine()).thenReturn(requestLine);
+ }
+
+ @Test
+ public void properties() {
+ ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { null, httpRequest }, rsc);
+
+ assertThat(adapter.getPropagationType(), is(PropagationType.HTTP));
+ assertThat(adapter.getReferenceType(), is(References.CHILD_OF));
+ assertThat(adapter.getFormat(), is(Format.Builtin.HTTP_HEADERS));
+ verifyZeroInteractions(object, rsc);
+ }
+
+ @Test
+ public void spanStarting() {
+ when(httpRequest.containsHeader(anyString())).thenReturn(false);
+
+ ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { null, httpRequest }, rsc);
+
+ assertThat(adapter.startClientSpan(), is(true));
+ }
+
+ @Test
+ public void spanStartingContainsHeader() {
+ when(httpRequest.containsHeader(anyString())).thenReturn(true);
+
+ ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { null, httpRequest }, rsc);
+
+ assertThat(adapter.startClientSpan(), is(false));
+ }
+
+ @Test
+ public void url() {
+ String uri = "uri";
+ when(requestLine.getUri()).thenReturn(uri);
+
+ ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { null, httpRequest }, rsc);
+
+ Map tags = adapter.getTags();
+ assertThat(tags.size(), is(1));
+ assertThat(tags, hasEntry(Tags.HTTP_URL.getKey(), uri));
+ verifyZeroInteractions(object, rsc);
+ }
+
+ @Test
+ public void method() {
+ String method = "get";
+ when(requestLine.getMethod()).thenReturn(method);
+
+ ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { null, httpRequest }, rsc);
+
+ Map tags = adapter.getTags();
+ assertThat(tags.size(), is(1));
+ assertThat(tags, hasEntry(Tags.HTTP_METHOD.getKey(), method));
+ verifyZeroInteractions(object, rsc);
+ }
+
+ @Test
+ public void nullRequestLine() {
+ when(httpRequest.getRequestLine()).thenReturn(null);
+
+ ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { null, httpRequest }, rsc);
+
+ Map tags = adapter.getTags();
+ assertThat(tags.size(), is(0));
+ verifyZeroInteractions(object, rsc);
+ }
+
+ @Test
+ public void baggageInjection() {
+ String key = "key";
+ String value = "value";
+
+ ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { null, httpRequest }, rsc);
+ adapter.getCarrier().put(key, value);
+
+ verify(httpRequest).setHeader(key, value);
+ verifyZeroInteractions(object, rsc);
+ }
+ }
+
+ public static class GetClientResponseAdapter extends ApacheHttpClientV40SensorTest {
+
+ @Mock
+ Object object;
+
+ @Mock
+ HttpResponse httpResponse;
+
+ @Mock
+ StatusLine statusLine;
+
+ @Test
+ public void responseNull() {
+ ResponseAdapter adapter = sensor.getClientResponseAdapter(object, null, null, rsc);
+
+ Map tags = adapter.getTags();
+ assertThat(tags.size(), is(0));
+ verifyZeroInteractions(object, rsc);
+ }
+
+ @Test
+ public void status() {
+ int status = 200;
+ when(statusLine.getStatusCode()).thenReturn(status);
+ when(httpResponse.getStatusLine()).thenReturn(statusLine);
+
+ ResponseAdapter adapter = sensor.getClientResponseAdapter(object, null, httpResponse, rsc);
+
+ Map tags = adapter.getTags();
+ assertThat(tags.size(), is(1));
+ assertThat(tags, hasEntry(Tags.HTTP_STATUS.getKey(), String.valueOf(status)));
+ verifyZeroInteractions(object, rsc);
+ }
+
+ @Test
+ public void statusNullStatusLine() {
+ when(httpResponse.getStatusLine()).thenReturn(null);
+
+ ResponseAdapter adapter = sensor.getClientResponseAdapter(object, null, httpResponse, rsc);
+
+ Map