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: + *

+ * + * @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: + *

+ * + * @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: + *

+ * + * @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 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 sensorClass) { */ @Test(dataProvider = "skippingSensors") public void skipSensorWithEnchancedExceptionSensor(Class 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 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 tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verifyZeroInteractions(object, rsc); + } + + } + + + interface HttpRequest { + RequestLine getRequestLine(); + void setHeader(String key, String value); + boolean containsHeader(String key); + }; + + interface HttpResponse { + StatusLine getStatusLine(); + }; + + interface RequestLine { + String getUri(); + String getMethod(); + }; + + interface StatusLine { + int getStatusCode(); + }; + +} \ No newline at end of file diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/JettyHttpClientV61SensorTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/JettyHttpClientV61SensorTest.java new file mode 100644 index 000000000..deed78fa1 --- /dev/null +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/JettyHttpClientV61SensorTest.java @@ -0,0 +1,235 @@ +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.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +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.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 JettyHttpClientV61SensorTest extends TestBase { + + @InjectMocks + JettyHttpClientV61Sensor sensor; + + @Mock + RegisteredSensorConfig rsc; + + public static class GetClientRequestAdapter extends JettyHttpClientV61SensorTest { + + @Mock + Object object; + + @Mock + HttpExchange httpExchange; + + @Mock + Scheme scheme; + + @Test + public void properties() { + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { httpExchange }, rsc); + + assertThat(adapter.getPropagationType(), is(PropagationType.HTTP)); + assertThat(adapter.getReferenceType(), is(References.FOLLOWS_FROM)); + assertThat(adapter.getFormat(), is(Format.Builtin.HTTP_HEADERS)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void spanStarting() { + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { httpExchange }, rsc); + + assertThat(adapter.startClientSpan(), is(true)); + } + + @Test + public void url() { + String uri = "/test"; + String schemeString = "http"; + String address = "localhost:8080"; + when(scheme.array()).thenReturn(schemeString.getBytes()); + when(httpExchange.getScheme()).thenReturn(scheme); + when(httpExchange.getAddress()).thenReturn(address); + when(httpExchange.getRequestURI()).thenReturn(uri); + + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { httpExchange }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_URL.getKey(), schemeString + "://" + address + uri)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void urlUriNull() { + String schemeString = "http"; + String address = "localhost:8080"; + when(scheme.array()).thenReturn(schemeString.getBytes()); + when(httpExchange.getScheme()).thenReturn(scheme); + when(httpExchange.getAddress()).thenReturn(address); + when(httpExchange.getRequestURI()).thenReturn(null); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { httpExchange }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags, hasEntry(Tags.HTTP_URL.getKey(), schemeString + "://" + address)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void urlAddressNull() { + String uri = "/test"; + String schemeString = "http"; + when(scheme.array()).thenReturn(schemeString.getBytes()); + when(httpExchange.getScheme()).thenReturn(scheme); + when(httpExchange.getAddress()).thenReturn(null); + when(httpExchange.getRequestURI()).thenReturn(uri); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { httpExchange }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_URL.getKey(), schemeString + "://" + uri)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void urlSchemeNull() { + String uri = "/test"; + String address = "localhost:8080"; + when(httpExchange.getScheme()).thenReturn(null); + when(httpExchange.getAddress()).thenReturn(address); + when(httpExchange.getRequestURI()).thenReturn(uri); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { httpExchange }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_URL.getKey(), address + uri)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void urlSchemeBytesNull() { + String uri = "/test"; + String address = "localhost:8080"; + when(scheme.array()).thenReturn(null); + when(httpExchange.getScheme()).thenReturn(scheme); + when(httpExchange.getAddress()).thenReturn(address); + when(httpExchange.getRequestURI()).thenReturn(uri); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { httpExchange }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_URL.getKey(), address + uri)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void urlAllNull() { + when(httpExchange.getScheme()).thenReturn(null); + when(httpExchange.getAddress()).thenReturn(null); + when(httpExchange.getRequestURI()).thenReturn(null); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { httpExchange }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void method() { + String method = "get"; + when(httpExchange.getMethod()).thenReturn(method); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { httpExchange }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_METHOD.getKey(), method)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void methodNull() { + when(httpExchange.getMethod()).thenReturn(null); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { httpExchange }, 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[] { httpExchange }, rsc); + adapter.getCarrier().put(key, value); + + verify(httpExchange).setRequestHeader(key, value); + verifyZeroInteractions(object, rsc); + } + } + + public static class GetClientResponseAdapter extends JettyHttpClientV61SensorTest { + + @Mock + Object object; + + @Mock + Object result; + + @Test + public void empty() { + ResponseAdapter adapter = sensor.getClientResponseAdapter(object, null, result, rsc); + + assertThat(adapter, is(not(nullValue()))); + assertThat(adapter.getTags().size(), is(0)); + verifyZeroInteractions(object, rsc); + } + + } + + interface HttpExchange { + Scheme getScheme(); + Object getAddress(); + Object getRequestURI(); + String getMethod(); + void setRequestHeader(String key, String value); + } + + interface Scheme { + byte[] array(); + } + +} \ No newline at end of file diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/SpringRestTemplateClientSensorTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/SpringRestTemplateClientSensorTest.java new file mode 100644 index 000000000..b8ddabde1 --- /dev/null +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/SpringRestTemplateClientSensorTest.java @@ -0,0 +1,253 @@ +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.anyObject; +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.net.URI; +import java.util.Map; + +import org.mockito.InjectMocks; +import org.mockito.Mock; +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 SpringRestTemplateClientSensorTest extends TestBase { + + @InjectMocks + SpringRestTemplateClientSensor sensor; + + @Mock + RegisteredSensorConfig rsc; + + @Mock + ClientHttpRequest httpRequest; + + public static class GetClientRequestAdapter extends SpringRestTemplateClientSensorTest { + + @Mock + Headers headers; + + @Test + public void properties() { + when(rsc.getTargetMethodName()).thenReturn("execute"); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(httpRequest, null, rsc); + + assertThat(adapter.getPropagationType(), is(PropagationType.HTTP)); + assertThat(adapter.getReferenceType(), is(References.CHILD_OF)); + assertThat(adapter.getFormat(), is(Format.Builtin.HTTP_HEADERS)); + verify(rsc).getTargetMethodName(); + verifyNoMoreInteractions(rsc); + } + + @Test + public void propertiesAsync() { + when(rsc.getTargetMethodName()).thenReturn("executeAsync"); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(httpRequest, null, rsc); + + assertThat(adapter.getPropagationType(), is(PropagationType.HTTP)); + assertThat(adapter.getReferenceType(), is(References.FOLLOWS_FROM)); + verify(rsc).getTargetMethodName(); + verifyNoMoreInteractions(rsc); + } + + @Test + public void spanStarting() { + when(httpRequest.getHeaders()).thenReturn(headers); + when(headers.containsKey(anyObject())).thenReturn(false); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(httpRequest, null, rsc); + + assertThat(adapter.startClientSpan(), is(true)); + } + + @Test + public void spanStartingContainsHeader() { + when(httpRequest.getHeaders()).thenReturn(headers); + when(headers.containsKey(anyObject())).thenReturn(true); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(httpRequest, null, rsc); + + assertThat(adapter.startClientSpan(), is(false)); + } + + @Test + public void spanStartingHeadersNull() { + when(httpRequest.getHeaders()).thenReturn(null); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(httpRequest, null, rsc); + + assertThat(adapter.startClientSpan(), is(true)); + } + + @Test + public void url() throws Exception { + String uri = "http://localhost"; + when(httpRequest.getURI()).thenReturn(new URI(uri)); + when(rsc.getTargetMethodName()).thenReturn("execute"); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(httpRequest, null, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_URL.getKey(), uri)); + verify(rsc).getTargetMethodName(); + verifyNoMoreInteractions(rsc); + } + + @Test + public void urlUriNull() { + when(httpRequest.getURI()).thenReturn(null); + when(rsc.getTargetMethodName()).thenReturn("execute"); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(httpRequest, null, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verify(rsc).getTargetMethodName(); + verifyNoMoreInteractions(rsc); + } + + @Test + public void method() throws Exception { + String method = "GET"; + when(httpRequest.getMethod()).thenReturn(method); + when(rsc.getTargetMethodName()).thenReturn("execute"); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(httpRequest, null, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_METHOD.getKey(), method)); + verify(rsc).getTargetMethodName(); + verifyNoMoreInteractions(rsc); + } + + @Test + public void methodNull() { + when(httpRequest.getMethod()).thenReturn(null); + when(rsc.getTargetMethodName()).thenReturn("execute"); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(httpRequest, null, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verify(rsc).getTargetMethodName(); + verifyNoMoreInteractions(rsc); + } + + @Test + public void baggageInjection() { + String key = "key"; + String value = "value"; + when(httpRequest.getHeaders()).thenReturn(headers); + when(rsc.getTargetMethodName()).thenReturn("execute"); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(httpRequest, null, rsc); + adapter.getCarrier().put(key, value); + + verify(headers).set(key, value); + verify(rsc).getTargetMethodName(); + verifyNoMoreInteractions(rsc); + } + + @Test + public void baggageInjectionHeadersNull() { + String key = "key"; + String value = "value"; + when(httpRequest.getHeaders()).thenReturn(null); + when(rsc.getTargetMethodName()).thenReturn("execute"); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(httpRequest, null, rsc); + adapter.getCarrier().put(key, value); + + verify(rsc).getTargetMethodName(); + verifyNoMoreInteractions(rsc); + } + } + + public static class GetClientResponseAdapter extends SpringRestTemplateClientSensorTest { + + @Mock + ClientHttpResponse httpResponse; + + @Test + public void responseNull() { + ResponseAdapter adapter = sensor.getClientResponseAdapter(httpRequest, null, null, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verify(rsc).getTargetMethodName(); + verifyZeroInteractions(httpRequest); + verifyNoMoreInteractions(rsc); + } + + @Test + public void status() { + int status = 200; + when(httpResponse.getRawStatusCode()).thenReturn(status); + when(rsc.getTargetMethodName()).thenReturn("execute"); + + ResponseAdapter adapter = sensor.getClientResponseAdapter(httpRequest, null, httpResponse, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_STATUS.getKey(), String.valueOf(status))); + verify(rsc).getTargetMethodName(); + verifyZeroInteractions(httpRequest); + verifyNoMoreInteractions(rsc); + } + + @Test + public void statusAsync() { + when(rsc.getTargetMethodName()).thenReturn("executeAsync"); + + ResponseAdapter adapter = sensor.getClientResponseAdapter(httpRequest, null, null, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verify(rsc).getTargetMethodName(); + verifyZeroInteractions(httpRequest); + verifyNoMoreInteractions(rsc); + } + + } + + + interface ClientHttpRequest { + Object getURI(); + Object getMethod(); + Headers getHeaders(); + }; + + interface Headers { + void set(String key, String value); + boolean containsKey(Object key); + } + + interface ClientHttpResponse { + int getRawStatusCode(); + }; + +} \ No newline at end of file diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/UrlConnectionSensorTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/UrlConnectionSensorTest.java new file mode 100644 index 000000000..a83554e15 --- /dev/null +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/http/UrlConnectionSensorTest.java @@ -0,0 +1,204 @@ +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.hamcrest.Matchers.nullValue; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doThrow; +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.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; + +import org.mockito.InjectMocks; +import org.mockito.Mock; +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 UrlConnectionSensorTest extends TestBase { + + @InjectMocks + UrlConnectionSensor sensor; + + @Mock + RegisteredSensorConfig rsc; + + @Mock + HttpURLConnection urlConnection; + + public static class GetClientRequestAdapter extends UrlConnectionSensorTest { + + public void properties() { + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(urlConnection, null, rsc); + + assertThat(adapter.getPropagationType(), is(PropagationType.HTTP)); + assertThat(adapter.getReferenceType(), is(References.CHILD_OF)); + assertThat(adapter.getFormat(), is(Format.Builtin.HTTP_HEADERS)); + verifyZeroInteractions(rsc); + } + + @Test + public void spanStarting() { + when(urlConnection.getRequestProperty(anyString())).thenReturn(null); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(urlConnection, null, rsc); + + assertThat(adapter.startClientSpan(), is(true)); + } + + @Test + public void spanStartingContainsHeader() { + when(urlConnection.getRequestProperty(anyString())).thenReturn("bla bla"); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(urlConnection, null, rsc); + + assertThat(adapter.startClientSpan(), is(false)); + } + + @Test + public void spanStartingAlreadyConnected() throws Exception { + doThrow(IllegalStateException.class).when(urlConnection).setRequestMethod(anyString()); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(urlConnection, null, rsc); + + assertThat(adapter.startClientSpan(), is(false)); + } + + @Test + public void url() throws MalformedURLException { + String uri = "http://localhost"; + when(urlConnection.getURL()).thenReturn(new URL(uri)); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(urlConnection, null, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_URL.getKey(), uri)); + verifyZeroInteractions(rsc); + } + + @Test + public void urlNull() { + when(urlConnection.getURL()).thenReturn(null); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(urlConnection, null, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verifyZeroInteractions(rsc); + } + + @Test + public void method() throws MalformedURLException { + String method = "GET"; + when(urlConnection.getRequestMethod()).thenReturn(method); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(urlConnection, null, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_METHOD.getKey(), method)); + verifyZeroInteractions(rsc); + } + + @Test + public void baggageInjection() { + String key = "key"; + String value = "value"; + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(urlConnection, null, rsc); + adapter.getCarrier().put(key, value); + + verify(urlConnection).setRequestProperty(key, value); + verifyZeroInteractions(rsc); + } + + @Test + public void notUrlConnection() throws IOException { + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(new Object(), null, rsc); + + assertThat(adapter, is(nullValue())); + } + } + + public static class GetClientResponseAdapter extends UrlConnectionSensorTest { + + @Mock + Object result; + + @Test + public void status() throws IOException { + int status = 200; + when(urlConnection.getResponseCode()).thenReturn(status); + when(rsc.getTargetMethodName()).thenReturn("getInputStream"); + + ResponseAdapter adapter = sensor.getClientResponseAdapter(urlConnection, null, result, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_STATUS.getKey(), String.valueOf(status))); + verify(rsc).getTargetMethodName(); + verifyNoMoreInteractions(rsc); + verifyZeroInteractions(result); + } + + @Test + public void statusIOException() throws IOException { + when(urlConnection.getResponseCode()).thenThrow(new IOException()); + when(rsc.getTargetMethodName()).thenReturn("getInputStream"); + + ResponseAdapter adapter = sensor.getClientResponseAdapter(urlConnection, null, result, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verify(rsc).getTargetMethodName(); + verifyNoMoreInteractions(rsc); + verifyZeroInteractions(result); + } + + @Test + public void notUrlConnection() throws IOException { + when(rsc.getTargetMethodName()).thenReturn("getInputStream"); + + ResponseAdapter adapter = sensor.getClientResponseAdapter(new Object(), null, result, rsc); + + assertThat(adapter, is(nullValue())); + verifyZeroInteractions(result, rsc); + } + + @Test + public void notInputStreamMethod() throws IOException { + when(rsc.getTargetMethodName()).thenReturn("connect"); + + ResponseAdapter adapter = sensor.getClientResponseAdapter(urlConnection, null, result, rsc); + + assertThat(adapter, is(nullValue())); + verify(rsc).getTargetMethodName(); + verifyNoMoreInteractions(rsc); + verifyZeroInteractions(result); + } + + } + +} \ No newline at end of file diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/mq/JmsRemoteClientSensorTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/mq/JmsRemoteClientSensorTest.java new file mode 100644 index 000000000..984291b15 --- /dev/null +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/client/mq/JmsRemoteClientSensorTest.java @@ -0,0 +1,179 @@ +package rocks.inspectit.agent.java.sensor.method.remote.client.mq; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.Map; + +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.testng.annotations.Test; + +import io.opentracing.References; +import io.opentracing.propagation.Format; +import io.opentracing.propagation.TextMap; +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.constants.ExtraTags; +import rocks.inspectit.shared.all.tracing.data.PropagationType; + +/** + * @author Ivan Senic + * + */ +@SuppressWarnings("PMD") +public class JmsRemoteClientSensorTest extends TestBase { + + @InjectMocks + JmsRemoteClientSensor sensor; + + @Mock + RegisteredSensorConfig rsc; + + public static class GetClientRequestAdapter extends JmsRemoteClientSensorTest { + + @Mock + Object object; + + @Mock + Message message; + + @Mock + Destination jmsDestination; + + @Test + public void properties() { + when(rsc.getParameterTypes()).thenReturn(Collections.singletonList("javax.jms.Message")); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { message }, rsc); + + assertThat(adapter.getPropagationType(), is(PropagationType.JMS)); + assertThat(adapter.getReferenceType(), is(References.FOLLOWS_FROM)); + assertThat(adapter.getFormat(), is(Format.Builtin.TEXT_MAP)); + verifyZeroInteractions(object); + } + + @Test + public void spanStarting() { + when(rsc.getParameterTypes()).thenReturn(Collections.singletonList("javax.jms.Message")); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { message }, rsc); + + assertThat(adapter.startClientSpan(), is(true)); + } + + @Test + public void destination() throws Exception { + String destination = "destination"; + when(message.getJMSDestination()).thenReturn(jmsDestination); + when(jmsDestination.toString()).thenReturn(destination); + when(rsc.getParameterTypes()).thenReturn(Collections.singletonList("javax.jms.Message")); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { message }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(ExtraTags.JMS_MESSAGE_DESTINATION, destination)); + verifyZeroInteractions(object); + } + + @Test + public void destinationNull() throws Exception { + when(message.getJMSDestination()).thenReturn(null); + when(rsc.getParameterTypes()).thenReturn(Collections.singletonList("javax.jms.Message")); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { message }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verifyZeroInteractions(object); + } + + @Test + public void destinationException() throws Exception { + when(message.getJMSDestination()).thenThrow(new Exception()); + when(rsc.getParameterTypes()).thenReturn(Collections.singletonList("javax.jms.Message")); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { message }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verifyZeroInteractions(object); + } + + @Test + public void messageId() throws Exception { + String id = "id"; + when(message.getJMSMessageID()).thenReturn(id); + when(rsc.getParameterTypes()).thenReturn(Collections.singletonList("javax.jms.Message")); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { message }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(ExtraTags.JMS_MESSAGE_ID, id)); + verifyZeroInteractions(object); + } + + @Test + public void messageIdException() throws Exception { + when(message.getJMSMessageID()).thenThrow(new Exception()); + when(rsc.getParameterTypes()).thenReturn(Collections.singletonList("javax.jms.Message")); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { message }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verifyZeroInteractions(object); + } + + @Test + public void baggageInjection() throws Exception { + String key = "key"; + String value = "value"; + when(rsc.getParameterTypes()).thenReturn(Collections.singletonList("javax.jms.Message")); + + ClientRequestAdapter adapter = sensor.getClientRequestAdapter(object, new Object[] { message }, rsc); + adapter.getCarrier().put(key, value); + + verify(message).setStringProperty(key, value); + verifyZeroInteractions(object); + } + } + + public static class GetClientResponseAdapter extends JmsRemoteClientSensorTest { + + @Mock + Object object; + + @Mock + Object result; + + @Test + public void empty() { + ResponseAdapter adapter = sensor.getClientResponseAdapter(object, null, result, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verifyZeroInteractions(object, result, rsc); + } + + } + + interface Message { + Destination getJMSDestination() throws Exception; + String getJMSMessageID() throws Exception; + void setStringProperty(String key, String value) throws Exception; + }; + + interface Destination { + }; + +} \ No newline at end of file diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/server/RemoteServerHookTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/server/RemoteServerHookTest.java new file mode 100644 index 000000000..041fb41f4 --- /dev/null +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/server/RemoteServerHookTest.java @@ -0,0 +1,180 @@ +package rocks.inspectit.agent.java.sensor.method.remote.server; + +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.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.ServerInterceptor; +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.shared.all.testbase.TestBase; +import rocks.inspectit.shared.all.tracing.data.ServerSpan; + +/** + * @author Ivan Senic + * + */ +@SuppressWarnings("PMD") +public class RemoteServerHookTest extends TestBase { + + @InjectMocks + RemoteServerHook hook; + + @Mock + ICoreService coreService; + + @Mock + ServerInterceptor serverInterceptor; + + @Mock + ServerAdapterProvider adapterProvider; + + @Mock + ServerRequestAdapter 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).getServerRequestAdapter(object, parameters, rsc); + doReturn(responseAdapter).when(adapterProvider).getServerResponseAdapter(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(false); + when(serverInterceptor.handleRequest(requestAdapter)).thenReturn(spanImpl); + when(serverInterceptor.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(ServerSpan.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(serverInterceptor).handleRequest(requestAdapter); + verify(serverInterceptor).handleResponse(spanImpl, responseAdapter); + verify(adapterProvider).getServerRequestAdapter(object, parameters, rsc); + verify(adapterProvider).getServerResponseAdapter(object, parameters, result, rsc); + verifyNoMoreInteractions(adapterProvider, serverInterceptor, 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).getServerRequestAdapter(object, parameters, rsc); + doReturn(responseAdapter).when(adapterProvider).getServerResponseAdapter(object, parameters, result, rsc); + when(serverInterceptor.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(serverInterceptor).handleRequest(requestAdapter); + verify(adapterProvider).getServerRequestAdapter(object, parameters, rsc); + verifyNoMoreInteractions(adapterProvider, serverInterceptor, coreService); + 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).getServerRequestAdapter(object, parameters, rsc); + doReturn(responseAdapter).when(adapterProvider).getServerResponseAdapter(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(false); + when(serverInterceptor.handleRequest(requestAdapter)).thenReturn(spanImpl); + when(serverInterceptor.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(ServerSpan.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(serverInterceptor).handleRequest(requestAdapter); + verify(serverInterceptor).handleResponse(spanImpl, responseAdapter); + verify(adapterProvider).getServerRequestAdapter(object, parameters, rsc); + verify(adapterProvider).getServerResponseAdapter(object, parameters, result, rsc); + verifyNoMoreInteractions(adapterProvider, serverInterceptor, coreService); + } + +} diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/server/http/JavaHttpRemoteServerSensorTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/server/http/JavaHttpRemoteServerSensorTest.java new file mode 100644 index 000000000..4efa167b1 --- /dev/null +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/server/http/JavaHttpRemoteServerSensorTest.java @@ -0,0 +1,166 @@ +package rocks.inspectit.agent.java.sensor.method.remote.server.http; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.testng.annotations.Test; + +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.ResponseAdapter; +import rocks.inspectit.agent.java.tracing.core.adapter.ServerRequestAdapter; +import rocks.inspectit.shared.all.testbase.TestBase; +import rocks.inspectit.shared.all.tracing.data.PropagationType; + +/** + * @author Ivan Senic + * + */ +@SuppressWarnings("PMD") +public class JavaHttpRemoteServerSensorTest extends TestBase { + + @InjectMocks + JavaHttpRemoteServerSensor sensor; + + @Mock + RegisteredSensorConfig rsc; + + @Mock + HttpServletRequest httpRequest; + + @Mock + HttpServletResponse httpResponse; + + public static class GetServerRequestAdapter extends JavaHttpRemoteServerSensorTest { + + @Mock + Object object; + + @Test + public void properties() { + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { httpRequest, httpResponse }, rsc); + + assertThat(adapter.getPropagationType(), is(PropagationType.HTTP)); + verifyZeroInteractions(object, httpResponse, rsc); + } + + @Test + public void uri() { + String uri = "uri"; + when(httpRequest.getRequestURI()).thenReturn(uri); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { httpRequest, httpResponse }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_URL.getKey(), uri)); + verifyZeroInteractions(object, httpResponse, rsc); + } + + @Test + public void uriNull() { + when(httpRequest.getRequestURI()).thenReturn(null); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { httpRequest, httpResponse }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verifyZeroInteractions(object, httpResponse, rsc); + } + + @Test + public void method() { + String method = "get"; + when(httpRequest.getMethod()).thenReturn(method); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { httpRequest, httpResponse }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_METHOD.getKey(), method)); + verifyZeroInteractions(object, httpResponse, rsc); + } + + @Test + public void methodNull() { + when(httpRequest.getMethod()).thenReturn(null); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { httpRequest, httpResponse }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verifyZeroInteractions(object, httpResponse, rsc); + } + + @Test + public void baggageExtraction() { + String key = "key"; + String value = "value"; + when(httpRequest.getHeader(key)).thenReturn(value); + doReturn(Collections.enumeration(Collections.singleton(key))).when(httpRequest).getHeaderNames(); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { httpRequest, httpResponse }, rsc); + + Entry next = adapter.getCarrier().iterator().next(); + assertThat(next.getKey(), is(key)); + assertThat(next.getValue(), is(value)); + assertThat(adapter.getCarrier().iterator().hasNext(), is(false)); + verifyZeroInteractions(object, httpResponse, rsc); + } + + @Test + public void baggageExtractionEnumerationEmpty() throws Exception { + doReturn(Collections.enumeration(Collections.emptyList())).when(httpRequest).getHeaderNames(); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { httpRequest, httpResponse }, rsc); + + assertThat(adapter.getCarrier().iterator().hasNext(), is(false)); + verifyZeroInteractions(object, httpResponse, rsc); + } + + @Test + public void baggageExtractionEnumerationNull() throws Exception { + doReturn(null).when(httpRequest).getHeaderNames(); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { httpRequest, httpResponse }, rsc); + + assertThat(adapter.getCarrier().iterator().hasNext(), is(false)); + verifyZeroInteractions(object, httpResponse, rsc); + } + } + + public static class GetServertResponseAdapter extends JavaHttpRemoteServerSensorTest { + + @Mock + Object object; + + @Test + public void status() { + int status = 200; + when(httpResponse.getStatus()).thenReturn(status); + + ResponseAdapter adapter = sensor.getServerResponseAdapter(object, new Object[] { httpRequest, httpResponse }, null, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(Tags.HTTP_STATUS.getKey(), String.valueOf(status))); + verifyZeroInteractions(object, rsc); + } + + } + +} \ No newline at end of file diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/server/manual/ManualRemoteServerSensorTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/server/manual/ManualRemoteServerSensorTest.java new file mode 100644 index 000000000..0e57a5cc3 --- /dev/null +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/server/manual/ManualRemoteServerSensorTest.java @@ -0,0 +1,68 @@ +package rocks.inspectit.agent.java.sensor.method.remote.server.manual; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Mockito.verifyZeroInteractions; + +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.tracing.core.adapter.ResponseAdapter; +import rocks.inspectit.agent.java.tracing.core.adapter.ServerRequestAdapter; +import rocks.inspectit.shared.all.testbase.TestBase; + +/** + * @author Ivan Senic + * + */ +public class ManualRemoteServerSensorTest extends TestBase { + + @InjectMocks + ManualRemoteServerSensor sensor; + + @Mock + RegisteredSensorConfig rsc; + + @Mock + Object object; + + public static class GetServerRequestAdapter extends ManualRemoteServerSensorTest { + + @Test + public void empty() { + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, null, rsc); + + assertThat(adapter.getPropagationType(), is(nullValue())); + assertThat(adapter.getTags().size(), is(0)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void baggageExtraction() { + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, null, rsc); + + assertThat(adapter.getCarrier(), is(nullValue())); + assertThat(adapter.getFormat(), is(nullValue())); + verifyZeroInteractions(object, rsc); + } + + } + + public static class GetServerResponseAdapter extends ManualRemoteServerSensorTest { + + @Mock + Object result; + + @Test + public void empty() { + ResponseAdapter adapter = sensor.getServerResponseAdapter(object, null, result, rsc); + + assertThat(adapter.getTags().size(), is(0)); + verifyZeroInteractions(object, result, rsc); + } + + } +} diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/server/mq/JmsListenerRemoteServerSensorTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/server/mq/JmsListenerRemoteServerSensorTest.java new file mode 100644 index 000000000..d06619db9 --- /dev/null +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/remote/server/mq/JmsListenerRemoteServerSensorTest.java @@ -0,0 +1,188 @@ +package rocks.inspectit.agent.java.sensor.method.remote.server.mq; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.Enumeration; +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.Format; +import io.opentracing.propagation.TextMap; +import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig; +import rocks.inspectit.agent.java.tracing.core.adapter.ResponseAdapter; +import rocks.inspectit.agent.java.tracing.core.adapter.ServerRequestAdapter; +import rocks.inspectit.shared.all.testbase.TestBase; +import rocks.inspectit.shared.all.tracing.constants.ExtraTags; +import rocks.inspectit.shared.all.tracing.data.PropagationType; + +/** + * @author Ivan Senic + * + */ +@SuppressWarnings("PMD") +public class JmsListenerRemoteServerSensorTest extends TestBase { + + @InjectMocks + JmsListenerRemoteServerSensor sensor; + + @Mock + RegisteredSensorConfig rsc; + + public static class GetServerRequestAdapter extends JmsListenerRemoteServerSensorTest { + + @Mock + Object object; + + @Mock + Message message; + + @Mock + Destination jmsDestination; + + @Test + public void properties() { + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { message }, rsc); + + assertThat(adapter.getPropagationType(), is(PropagationType.JMS)); + assertThat(adapter.getFormat(), is(Format.Builtin.TEXT_MAP)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void destination() throws Exception { + String destination = "destination"; + when(message.getJMSDestination()).thenReturn(jmsDestination); + when(jmsDestination.toString()).thenReturn(destination); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { message }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(ExtraTags.JMS_MESSAGE_DESTINATION, destination)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void destinationNull() throws Exception { + when(message.getJMSDestination()).thenReturn(null); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { message }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void destinationException() throws Exception { + when(message.getJMSDestination()).thenThrow(new Exception()); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { message }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void messageId() throws Exception { + String id = "id"; + when(message.getJMSMessageID()).thenReturn(id); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { message }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(1)); + assertThat(tags, hasEntry(ExtraTags.JMS_MESSAGE_ID, id)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void messageIdException() throws Exception { + when(message.getJMSMessageID()).thenThrow(new Exception()); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { message }, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void baggageExtraction() throws Exception { + String key = "key"; + String value = "value"; + when(message.getStringProperty(key)).thenReturn(value); + doReturn(Collections.enumeration(Collections.singleton(key))).when(message).getPropertyNames(); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { message }, rsc); + + Entry next = adapter.getCarrier().iterator().next(); + assertThat(next.getKey(), is(key)); + assertThat(next.getValue(), is(value)); + assertThat(adapter.getCarrier().iterator().hasNext(), is(false)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void baggageExtractionEnumerationEmpty() throws Exception { + doReturn(Collections.enumeration(Collections.emptyList())).when(message).getPropertyNames(); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { message }, rsc); + + assertThat(adapter.getCarrier().iterator().hasNext(), is(false)); + verifyZeroInteractions(object, rsc); + } + + @Test + public void baggageExtractionEnumerationNull() throws Exception { + doReturn(null).when(message).getPropertyNames(); + + ServerRequestAdapter adapter = sensor.getServerRequestAdapter(object, new Object[] { message }, rsc); + + assertThat(adapter.getCarrier().iterator().hasNext(), is(false)); + verifyZeroInteractions(object, rsc); + } + } + + public static class GetServerResponseAdapter extends JmsListenerRemoteServerSensorTest { + + @Mock + Object object; + + @Mock + Object result; + + @Test + public void empty() { + ResponseAdapter adapter = sensor.getServerResponseAdapter(object, null, result, rsc); + + Map tags = adapter.getTags(); + assertThat(tags.size(), is(0)); + verifyZeroInteractions(object, result, rsc); + } + + } + + + interface Message { + Destination getJMSDestination() throws Exception; + String getJMSMessageID() throws Exception; + String getStringProperty(String key); + Enumeration getPropertyNames(); + }; + + interface Destination { + }; + +} \ No newline at end of file diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/special/ClassLoadingDelegationHookTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/special/ClassLoadingDelegationHookTest.java index dd0f527a3..8f7fabb15 100644 --- a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/special/ClassLoadingDelegationHookTest.java +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/sensor/method/special/ClassLoadingDelegationHookTest.java @@ -116,6 +116,16 @@ public void wrongParameterType() { verifyZeroInteractions(object, ssc); } + @Test + public void opentracingClass() throws Exception { + Object[] parameters = new String[] { io.opentracing.Tracer.class.getName() }; + + Object result = hook.beforeBody(METHOD_ID, object, parameters, ssc); + + assertThat(result, is((Object) io.opentracing.Tracer.class)); + verifyZeroInteractions(object, ssc); + } + @Test public void reflectAsmClassLoader() throws Exception { Class classLoaderClass = Class.forName("com.esotericsoftware.reflectasm.AccessClassLoader"); diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/ClientInterceptorTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/ClientInterceptorTest.java new file mode 100644 index 000000000..d5441d8d4 --- /dev/null +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/ClientInterceptorTest.java @@ -0,0 +1,184 @@ +package rocks.inspectit.agent.java.tracing.core; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyString; +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 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.sdk.opentracing.internal.impl.SpanBuilderImpl; +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.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.testbase.TestBase; +import rocks.inspectit.shared.all.tracing.constants.ExtraTags; +import rocks.inspectit.shared.all.tracing.data.PropagationType; + +/** + * @author Ivan Senic + * + */ +@SuppressWarnings("PMD") +public class ClientInterceptorTest extends TestBase { + + @InjectMocks + ClientInterceptor interceptor; + + @Mock + TracerImpl tracer; + + @Mock + SpanImpl span; + + public static class HandleRequest extends ClientInterceptorTest { + + @Mock + SpanBuilderImpl spanBuilder; + + @Mock + SpanContextImpl context; + + @Mock + ClientRequestAdapter requestAdapter; + + @Mock + TextMap carrier; + + @BeforeMethod + public void setup() { + when(requestAdapter.getFormat()).thenReturn(Format.Builtin.TEXT_MAP); + when(requestAdapter.getCarrier()).thenReturn(carrier); + when(tracer.buildSpan(anyString(), anyString(), anyBoolean())).thenReturn(spanBuilder); + when(spanBuilder.start()).thenReturn(span); + when(span.context()).thenReturn(context); + } + + @Test + public void happyPath() { + when(requestAdapter.startClientSpan()).thenReturn(true); + when(requestAdapter.getTags()).thenReturn(Collections. singletonMap(Tags.HTTP_URL.getKey(), "value")); + when(requestAdapter.getPropagationType()).thenReturn(PropagationType.HTTP); + when(requestAdapter.getReferenceType()).thenReturn(References.CHILD_OF); + + SpanImpl result = interceptor.handleRequest(requestAdapter); + + assertThat(result, is(span)); + verify(tracer).buildSpan(null, References.CHILD_OF, true); + verify(tracer).inject(context, Format.Builtin.TEXT_MAP, carrier); + verify(spanBuilder).doNotReport(); + verify(spanBuilder).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT); + verify(spanBuilder).withTag(ExtraTags.PROPAGATION_TYPE, PropagationType.HTTP.toString()); + verify(spanBuilder).withTag(Tags.HTTP_URL.getKey(), "value"); + verify(spanBuilder).start(); + verify(span).context(); + verifyNoMoreInteractions(tracer, spanBuilder, span); + verifyZeroInteractions(context); + } + + @Test + public void spanShouldNotStart() { + when(requestAdapter.startClientSpan()).thenReturn(false); + + SpanImpl result = interceptor.handleRequest(requestAdapter); + + assertThat(result, is(nullValue())); + } + + @Test + public void tagsNull() { + when(requestAdapter.startClientSpan()).thenReturn(true); + when(requestAdapter.getTags()).thenReturn(null); + when(requestAdapter.getPropagationType()).thenReturn(PropagationType.HTTP); + when(requestAdapter.getReferenceType()).thenReturn(References.CHILD_OF); + + SpanImpl result = interceptor.handleRequest(requestAdapter); + + assertThat(result, is(span)); + verify(tracer).buildSpan(null, References.CHILD_OF, true); + verify(tracer).inject(context, Format.Builtin.TEXT_MAP, carrier); + verify(spanBuilder).doNotReport(); + verify(spanBuilder).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT); + verify(spanBuilder).withTag(ExtraTags.PROPAGATION_TYPE, PropagationType.HTTP.toString()); + verify(spanBuilder).start(); + verify(span).context(); + verifyNoMoreInteractions(tracer, spanBuilder, span); + verifyZeroInteractions(context); + } + + @Test + public void propagationNull() { + when(requestAdapter.startClientSpan()).thenReturn(true); + when(requestAdapter.getPropagationType()).thenReturn(null); + when(requestAdapter.getReferenceType()).thenReturn(References.CHILD_OF); + + SpanImpl result = interceptor.handleRequest(requestAdapter); + + assertThat(result, is(span)); + verify(tracer).buildSpan(null, References.CHILD_OF, true); + verify(tracer).inject(context, Format.Builtin.TEXT_MAP, carrier); + verify(spanBuilder).doNotReport(); + verify(spanBuilder).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT); + verify(spanBuilder).start(); + verify(span).context(); + verifyNoMoreInteractions(tracer, spanBuilder, span); + verifyZeroInteractions(context); + } + } + + public static class HandleResponse extends ClientInterceptorTest { + + @Mock + ResponseAdapter responseAdapter; + + @Test + public void happyPath() { + when(responseAdapter.getTags()).thenReturn(Collections. singletonMap(Tags.HTTP_STATUS.getKey(), "200")); + + SpanImpl result = interceptor.handleResponse(span, responseAdapter); + + assertThat(result, is(span)); + verify(span).setTag(Tags.HTTP_STATUS.getKey(), "200"); + verify(span).finish(); + verifyNoMoreInteractions(span); + verifyZeroInteractions(tracer); + } + + @Test + public void noRequestHandled() { + SpanImpl result = interceptor.handleResponse(null, responseAdapter); + + assertThat(result, is(nullValue())); + verifyZeroInteractions(tracer, span); + } + + @Test + public void tagsNull() { + when(responseAdapter.getTags()).thenReturn(null); + + interceptor.handleResponse(span, responseAdapter); + + verify(span).finish(); + verifyNoMoreInteractions(span); + verifyZeroInteractions(tracer); + } + + + } +} diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/CoreServiceReporterTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/CoreServiceReporterTest.java new file mode 100644 index 000000000..f989ce477 --- /dev/null +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/CoreServiceReporterTest.java @@ -0,0 +1,59 @@ +package rocks.inspectit.agent.java.tracing.core; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +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.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.shared.all.testbase.TestBase; +import rocks.inspectit.shared.all.tracing.data.AbstractSpan; + +/** + * @author Ivan Senic + * + */ +@SuppressWarnings("PMD") +public class CoreServiceReporterTest extends TestBase { + + @InjectMocks + CoreServiceReporter reporter; + + @Mock + IPlatformManager platformManager; + + @Mock + ICoreService coreService; + + public static class Report extends CoreServiceReporterTest { + + @Test + public void happyPath() throws Exception { + long spanId = 13L; + long platformId = 17L; + SpanImpl span = mock(SpanImpl.class); + SpanContextImpl context = mock(SpanContextImpl.class); + when(context.getId()).thenReturn(spanId); + when(span.context()).thenReturn(context); + when(platformManager.getPlatformId()).thenReturn(platformId); + + reporter.report(span); + + ArgumentCaptor captor = ArgumentCaptor.forClass(AbstractSpan.class); + verify(coreService).addMethodSensorData(eq(0L), eq(0L), eq(String.valueOf(spanId)), captor.capture()); + assertThat(captor.getValue().getPlatformIdent(), is(platformId)); + assertThat(captor.getValue().getSensorTypeIdent(), is(0L)); + assertThat(captor.getValue().getMethodIdent(), is(0L)); + } + } +} diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/ServerInterceptorTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/ServerInterceptorTest.java new file mode 100644 index 000000000..90ce5086f --- /dev/null +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/ServerInterceptorTest.java @@ -0,0 +1,184 @@ +package rocks.inspectit.agent.java.tracing.core; + +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.Collections; + +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import io.opentracing.propagation.Format; +import io.opentracing.propagation.TextMap; +import io.opentracing.tag.Tags; +import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanBuilderImpl; +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.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.testbase.TestBase; +import rocks.inspectit.shared.all.tracing.constants.ExtraTags; +import rocks.inspectit.shared.all.tracing.data.PropagationType; + +/** + * @author Ivan Senic + * + */ +@SuppressWarnings("PMD") +public class ServerInterceptorTest extends TestBase { + + @InjectMocks + ServerInterceptor interceptor; + + @Mock + TracerImpl tracer; + + @Mock + SpanImpl span; + + public static class HandleRequest extends ServerInterceptorTest { + + @Mock + ServerRequestAdapter requestAdapter; + + @Mock + TextMap carrier; + + @Mock + SpanBuilderImpl spanBuilder; + + @Mock + SpanContextImpl context; + + @BeforeMethod + public void setup() { + when(requestAdapter.getFormat()).thenReturn(Format.Builtin.TEXT_MAP); + when(requestAdapter.getCarrier()).thenReturn(carrier); + when(tracer.buildSpan()).thenReturn(spanBuilder); + when(spanBuilder.start()).thenReturn(span); + } + + @Test + public void happyPath() { + when(requestAdapter.getPropagationType()).thenReturn(PropagationType.HTTP); + when(requestAdapter.getTags()).thenReturn(Collections. singletonMap(Tags.HTTP_URL.getKey(), "value")); + when(tracer.extract(Format.Builtin.TEXT_MAP, carrier)).thenReturn(context); + + SpanImpl result = interceptor.handleRequest(requestAdapter); + + assertThat(result, is(span)); + verify(tracer).buildSpan(); + verify(tracer).extract(Format.Builtin.TEXT_MAP, carrier); + verify(spanBuilder).asChildOf(context); + verify(spanBuilder).doNotReport(); + verify(spanBuilder).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER); + verify(spanBuilder).withTag(ExtraTags.PROPAGATION_TYPE, PropagationType.HTTP.toString()); + verify(spanBuilder).withTag(Tags.HTTP_URL.getKey(), "value"); + verify(spanBuilder).start(); + verifyNoMoreInteractions(tracer, spanBuilder); + verifyZeroInteractions(span, context); + } + + @Test + public void noTracePassed() { + when(tracer.extract(Format.Builtin.TEXT_MAP, carrier)).thenReturn(null); + + SpanImpl result = interceptor.handleRequest(requestAdapter); + + assertThat(result, is(span)); + verify(tracer).buildSpan(); + verify(tracer).extract(Format.Builtin.TEXT_MAP, carrier); + verify(spanBuilder).asChildOf((SpanContextImpl) null); + verify(spanBuilder).doNotReport(); + verify(spanBuilder).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER); + verify(spanBuilder).start(); + verifyNoMoreInteractions(tracer, spanBuilder); + verifyZeroInteractions(span, context); + } + + @Test + public void tagsNull() { + when(requestAdapter.getTags()).thenReturn(null); + when(requestAdapter.getPropagationType()).thenReturn(PropagationType.HTTP); + when(tracer.extract(Format.Builtin.TEXT_MAP, carrier)).thenReturn(context); + + SpanImpl result = interceptor.handleRequest(requestAdapter); + + assertThat(result, is(span)); + verify(tracer).buildSpan(); + verify(tracer).extract(Format.Builtin.TEXT_MAP, carrier); + verify(spanBuilder).asChildOf(context); + verify(spanBuilder).doNotReport(); + verify(spanBuilder).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER); + verify(spanBuilder).withTag(ExtraTags.PROPAGATION_TYPE, PropagationType.HTTP.toString()); + verify(spanBuilder).start(); + verifyNoMoreInteractions(tracer, spanBuilder); + verifyZeroInteractions(span, context); + } + + @Test + public void propagationNull() { + when(requestAdapter.getPropagationType()).thenReturn(null); + when(tracer.extract(Format.Builtin.TEXT_MAP, carrier)).thenReturn(context); + + SpanImpl result = interceptor.handleRequest(requestAdapter); + + assertThat(result, is(span)); + verify(tracer).buildSpan(); + verify(tracer).extract(Format.Builtin.TEXT_MAP, carrier); + verify(spanBuilder).asChildOf(context); + verify(spanBuilder).doNotReport(); + verify(spanBuilder).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER); + verify(spanBuilder).start(); + verifyNoMoreInteractions(tracer, spanBuilder); + verifyZeroInteractions(span, context); + } + } + + public static class HandleResponse extends ServerInterceptorTest { + + @Mock + ResponseAdapter responseAdapter; + + @Test + public void happyPath() { + when(responseAdapter.getTags()).thenReturn(Collections. singletonMap(Tags.HTTP_STATUS.getKey(), "200")); + + SpanImpl result = interceptor.handleResponse(span, responseAdapter); + + assertThat(result, is(span)); + verify(span).setTag(Tags.HTTP_STATUS.getKey(), "200"); + verify(span).finish(); + verifyNoMoreInteractions(span); + verifyZeroInteractions(tracer); + } + + @Test + public void noRequestHandled() { + SpanImpl result = interceptor.handleResponse(null, responseAdapter); + + assertThat(result, is(nullValue())); + } + + @Test + public void tagsNull() { + when(responseAdapter.getTags()).thenReturn(null); + + interceptor.handleResponse(span, responseAdapter); + + verify(span).finish(); + verifyNoMoreInteractions(span); + verifyZeroInteractions(tracer); + } + + } + +} diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/transformer/SpanContextTransformerTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/transformer/SpanContextTransformerTest.java new file mode 100644 index 000000000..9e3f093c0 --- /dev/null +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/transformer/SpanContextTransformerTest.java @@ -0,0 +1,56 @@ +package rocks.inspectit.agent.java.tracing.core.transformer; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +import java.util.Collections; + +import org.testng.annotations.Test; + +import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanContextImpl; +import rocks.inspectit.shared.all.tracing.data.SpanIdent; + +/** + * @author Ivan Senic + * + */ +public class SpanContextTransformerTest { + + public static class ContextToSpanIdent extends SpanContextTransformerTest { + + @Test + public void happyPath() { + SpanContextImpl context = new SpanContextImpl(1, 2, 3, null, Collections. emptyMap()); + + SpanIdent spanIdent = SpanContextTransformer.transformSpanContext(context); + + assertThat(spanIdent.getId(), is(1L)); + assertThat(spanIdent.getTraceId(), is(2L)); + assertThat(spanIdent.getParentId(), is(3L)); + } + + @Test + public void contextNull() { + SpanIdent spanIdent = SpanContextTransformer.transformSpanContext(null); + + assertThat(spanIdent, is(nullValue())); + } + + @Test + public void transformerInterface() { + SpanContextImpl context = new SpanContextImpl(1, 2, 3, null, Collections. emptyMap()); + + SpanIdent spanIdent = SpanContextTransformer.INSTANCE.transform(context); + + assertThat(spanIdent.getId(), is(1L)); + assertThat(spanIdent.getTraceId(), is(2L)); + assertThat(spanIdent.getParentId(), is(3L)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void transformerInterfaceWrongClass() { + SpanContextTransformer.INSTANCE.transform("some string"); + } + } +} diff --git a/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/transformer/SpanTransformerTest.java b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/transformer/SpanTransformerTest.java new file mode 100644 index 000000000..2e5727254 --- /dev/null +++ b/inspectit.agent.java/src/test/java/rocks/inspectit/agent/java/tracing/core/transformer/SpanTransformerTest.java @@ -0,0 +1,172 @@ +package rocks.inspectit.agent.java.tracing.core.transformer; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Mockito.when; + +import java.util.Collections; + +import org.mockito.Mock; +import org.testng.annotations.Test; + +import io.opentracing.References; +import io.opentracing.tag.Tags; +import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanContextImpl; +import rocks.inspectit.agent.java.sdk.opentracing.internal.impl.SpanImpl; +import rocks.inspectit.shared.all.testbase.TestBase; +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; + +/** + * @author Ivan Senic + * + */ +@SuppressWarnings("PMD") +public class SpanTransformerTest extends TestBase { + + public static class TransformSpan extends SpanTransformerTest { + + @Mock + SpanImpl spanImpl; + + @Test + public void spanNull() { + AbstractSpan span = SpanTransformer.transformSpan(null); + + assertThat(span, is(nullValue())); + } + + @Test + public void contextNull() { + when(spanImpl.context()).thenReturn(null); + + AbstractSpan span = SpanTransformer.transformSpan(spanImpl); + + assertThat(span, is(nullValue())); + } + + @Test + public void ident() { + when(spanImpl.context()).thenReturn(new SpanContextImpl(1, 2, 3, null, Collections. emptyMap())); + + AbstractSpan span = SpanTransformer.transformSpan(spanImpl); + + SpanIdent spanIdent = span.getSpanIdent(); + assertThat(spanIdent.getId(), is(1L)); + assertThat(spanIdent.getTraceId(), is(2L)); + assertThat(spanIdent.getParentId(), is(3L)); + } + + @Test + public void client() { + when(spanImpl.context()).thenReturn(SpanContextImpl.build()); + when(spanImpl.isClient()).thenReturn(true); + + AbstractSpan span = SpanTransformer.transformSpan(spanImpl); + + assertThat(span, is(instanceOf(ClientSpan.class))); + } + + @Test + public void server() { + when(spanImpl.context()).thenReturn(SpanContextImpl.build()); + when(spanImpl.isClient()).thenReturn(false); + + AbstractSpan span = SpanTransformer.transformSpan(spanImpl); + + assertThat(span, is(instanceOf(ServerSpan.class))); + } + + @Test + public void reference() { + when(spanImpl.context()).thenReturn(SpanContextImpl.build(SpanContextImpl.build(), References.FOLLOWS_FROM, Collections. emptyMap())); + when(spanImpl.isClient()).thenReturn(false); + + AbstractSpan span = SpanTransformer.transformSpan(spanImpl); + + assertThat(span.getReferenceType(), is(References.FOLLOWS_FROM)); + } + + @Test + public void propagation() { + when(spanImpl.context()).thenReturn(SpanContextImpl.build()); + when(spanImpl.getTags()).thenReturn(Collections.singletonMap(ExtraTags.PROPAGATION_TYPE, PropagationType.JMS.toString())); + + AbstractSpan span = SpanTransformer.transformSpan(spanImpl); + + assertThat(span.getPropagationType(), is(PropagationType.JMS)); + assertThat(span.getTags().size(), is(0)); + } + + @Test + public void tag() { + when(spanImpl.context()).thenReturn(SpanContextImpl.build()); + when(spanImpl.getTags()).thenReturn(Collections.singletonMap("key", "value")); + + AbstractSpan span = SpanTransformer.transformSpan(spanImpl); + + assertThat(span.getTags().size(), is(1)); + assertThat(span.getTags(), hasEntry("key", "value")); + } + + @Test + public void spanKindIgnored() { + when(spanImpl.context()).thenReturn(SpanContextImpl.build()); + when(spanImpl.getTags()).thenReturn(Collections.singletonMap(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)); + + AbstractSpan span = SpanTransformer.transformSpan(spanImpl); + + assertThat(span.getTags().size(), is(0)); + } + + @Test + public void time() { + long micros = 123456L; + when(spanImpl.context()).thenReturn(SpanContextImpl.build()); + when(spanImpl.getStartTimeMicros()).thenReturn(micros); + + AbstractSpan span = SpanTransformer.transformSpan(spanImpl); + + assertThat(span.getTimeStamp(), is(not(nullValue()))); + assertThat(span.getTimeStamp().getTime(), is(123L)); + } + + @Test + public void duration() { + double durationMicros = 123456.8856d; + when(spanImpl.context()).thenReturn(SpanContextImpl.build()); + when(spanImpl.getDuration()).thenReturn(durationMicros); + + AbstractSpan span = SpanTransformer.transformSpan(spanImpl); + + assertThat(span.getTimeStamp(), is(not(nullValue()))); + assertThat(span.getDuration(), is(durationMicros / 1000.d)); + } + + @Test + public void transformerInterface() { + when(spanImpl.context()).thenReturn(new SpanContextImpl(1, 2, 3, null, Collections. emptyMap())); + + AbstractSpan span = SpanTransformer.INSTANCE.transform(spanImpl); + + SpanIdent spanIdent = span.getSpanIdent(); + assertThat(spanIdent.getId(), is(1L)); + assertThat(spanIdent.getTraceId(), is(2L)); + assertThat(spanIdent.getParentId(), is(3L)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void transformerInterfaceWrongClass() { + SpanTransformer.INSTANCE.transform("some string"); + } + } + +} diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/ejb_v2.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/ejb_v2.xml index 6d7088c59..322c2491a 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/ejb_v2.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/ejb_v2.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/ejb_v3.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/ejb_v3.xml index 5edbf3dd7..a0e4973dd 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/ejb_v3.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/ejb_v3.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/exclude-classes.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/exclude-classes.xml index 09ce15188..dec1f5e08 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/exclude-classes.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/exclude-classes.xml @@ -1,12 +1,13 @@ - - + + @@ -15,6 +16,8 @@ + + \ No newline at end of file diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/hibernate.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/hibernate.xml index 23a43e372..70cc1b042 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/hibernate.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/hibernate.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/http.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/http.xml index 273acdbb8..d1b40bb1c 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/http.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/http.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/jpa.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/jpa.xml index 27164ac71..d9fb2684a 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/jpa.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/jpa.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/jsf.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/jsf.xml index cf80498b3..d29c35eff 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/jsf.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/jsf.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/jta.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/jta.xml index a8b3310e6..2496603af 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/jta.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/jta.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/logging-log4j.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/logging-log4j.xml index dba38c55c..c03083a2c 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/logging-log4j.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/logging-log4j.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/remote-http.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/remote-http.xml new file mode 100644 index 000000000..cff159546 --- /dev/null +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/remote-http.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/remote-jms.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/remote-jms.xml new file mode 100644 index 000000000..1b14e1030 --- /dev/null +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/remote-jms.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/sql-parameters.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/sql-parameters.xml index 375b48ba3..314909756 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/sql-parameters.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/sql-parameters.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/sql.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/sql.xml index bed1f6d46..bf7980be2 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/sql.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/sql.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/struts.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/struts.xml index f284acf6c..590189599 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/struts.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/struts.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/trinidad.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/trinidad.xml index 4de873ac5..161e31e55 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/trinidad.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/trinidad.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/webservice.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/webservice.xml index 8825155ed..7df2f4e36 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/webservice.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/webservice.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/profiles/common/websocket.xml b/inspectit.server/src/main/external-resources/ci/profiles/common/websocket.xml index 5b71500f6..c5383047e 100644 --- a/inspectit.server/src/main/external-resources/ci/profiles/common/websocket.xml +++ b/inspectit.server/src/main/external-resources/ci/profiles/common/websocket.xml @@ -1,5 +1,5 @@ - diff --git a/inspectit.server/src/main/external-resources/ci/schema/ciSchema.xsd b/inspectit.server/src/main/external-resources/ci/schema/ciSchema.xsd index 31823c31e..6be20642d 100644 --- a/inspectit.server/src/main/external-resources/ci/schema/ciSchema.xsd +++ b/inspectit.server/src/main/external-resources/ci/schema/ciSchema.xsd @@ -95,6 +95,22 @@ + + + + + + + + + + + + + + + + @@ -503,6 +519,14 @@ + + + + + + + + @@ -542,6 +566,11 @@ + + + + + @@ -550,11 +579,6 @@ - - - - - @@ -687,6 +711,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inspectit.server/src/main/external-resources/ci/schema/migration/0004-remote-sensors-types-environment.xml b/inspectit.server/src/main/external-resources/ci/schema/migration/0004-remote-sensors-types-environment.xml new file mode 100644 index 000000000..cbd33e318 --- /dev/null +++ b/inspectit.server/src/main/external-resources/ci/schema/migration/0004-remote-sensors-types-environment.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inspectit.server/src/test/java/rocks/inspectit/server/cache/impl/MemoryCalculationTest.java b/inspectit.server/src/test/java/rocks/inspectit/server/cache/impl/MemoryCalculationTest.java index 309ac537d..439884597 100644 --- a/inspectit.server/src/test/java/rocks/inspectit/server/cache/impl/MemoryCalculationTest.java +++ b/inspectit.server/src/test/java/rocks/inspectit/server/cache/impl/MemoryCalculationTest.java @@ -48,6 +48,8 @@ import rocks.inspectit.shared.all.communication.data.SystemInformationData; import rocks.inspectit.shared.all.communication.data.ThreadInformationData; import rocks.inspectit.shared.all.communication.data.TimerData; +import rocks.inspectit.shared.all.tracing.data.ClientSpan; +import rocks.inspectit.shared.all.tracing.data.ServerSpan; import rocks.inspectit.shared.all.util.UnderlyingSystemInfo; import rocks.inspectit.shared.all.util.UnderlyingSystemInfo.JvmProvider; @@ -72,7 +74,8 @@ public class MemoryCalculationTest extends AbstractTestNGLogSupport { public static final Object[][] TESTING_CLASSES = new Object[][] { { TestDefaultData.class }, { TestMethodSensorData.class }, { TestInvocationAwareData.class }, { TimerData.class }, { SqlStatementData.class }, { ExceptionSensorData.class }, { InvocationSequenceData.class }, { ClassLoadingInformationData.class }, { CompilationInformationData.class }, { MemoryInformationData.class }, { RuntimeInformationData.class }, { SystemInformationData.class }, { ThreadInformationData.class }, { HttpTimerData.class }, - { AggregatedExceptionSensorData.class }, { AggregatedHttpTimerData.class }, { AggregatedSqlStatementData.class }, { AggregatedTimerData.class } }; + { AggregatedExceptionSensorData.class }, { AggregatedHttpTimerData.class }, { AggregatedSqlStatementData.class }, { AggregatedTimerData.class }, { ClientSpan.class }, + { ServerSpan.class } }; /** * Amount that we add to each hash map because of the entry, key and value set safety. diff --git a/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/communication/data/HttpTimerData.java b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/communication/data/HttpTimerData.java index 7f5f9df25..5fff18d0d 100644 --- a/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/communication/data/HttpTimerData.java +++ b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/communication/data/HttpTimerData.java @@ -31,7 +31,7 @@ public class HttpTimerData extends TimerData { /** * The default header for tagged requests. */ - public static final String INSPECTIT_TAGGING_HEADER = "inspectit"; + public static final String INSPECTIT_TAGGING_HEADER = "x-inspectit-tag"; /** * String used to represent multiple request methods in an aggregation. diff --git a/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/communication/data/InvocationSequenceData.java b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/communication/data/InvocationSequenceData.java index a47e02526..65d10c053 100644 --- a/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/communication/data/InvocationSequenceData.java +++ b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/communication/data/InvocationSequenceData.java @@ -12,6 +12,7 @@ import rocks.inspectit.shared.all.cmr.cache.IObjectSizes; import rocks.inspectit.shared.all.communication.MethodSensorData; +import rocks.inspectit.shared.all.tracing.data.SpanIdent; /** * The invocation sequence data object which is used to store the path of method invocations from @@ -67,6 +68,13 @@ public class InvocationSequenceData extends MethodSensorData { @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) private LoggingData loggingData; + /** + * The information about the span the invocation is belonging to or invocation is calling to. + * Can be null. + */ + @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + private SpanIdent spanIdent; + /** * The position if parent sequence is not null. */ @@ -236,6 +244,25 @@ public void setLoggingData(LoggingData loggingData) { this.loggingData = loggingData; } + /** + * Gets {@link #spanIdent}. + * + * @return {@link #spanIdent} + */ + public SpanIdent getSpanIdent() { + return this.spanIdent; + } + + /** + * Sets {@link #spanIdent}. + * + * @param spanIdent + * New value for {@link #spanIdent} + */ + public void setSpanIdent(SpanIdent spanIdent) { + this.spanIdent = spanIdent; + } + /** * @param position * the position to set @@ -433,6 +460,7 @@ public int hashCode() { result = (prime * result) + ((sqlStatementData == null) ? 0 : sqlStatementData.hashCode()); result = (prime * result) + ((timerData == null) ? 0 : timerData.hashCode()); result = (prime * result) + ((loggingData == null) ? 0 : loggingData.hashCode()); + result = (prime * result) + ((spanIdent == null) ? 0 : spanIdent.hashCode()); result = (prime * result) + applicationId; result = (prime * result) + businessTransactionId; return result; @@ -481,6 +509,13 @@ public boolean equals(Object obj) { } else if (!loggingData.equals(other.loggingData)) { return false; } + if (spanIdent == null) { + if (other.spanIdent != null) { + return false; + } + } else if (!spanIdent.equals(other.spanIdent)) { + return false; + } if (applicationId != other.applicationId) { return false; } @@ -496,10 +531,11 @@ public boolean equals(Object obj) { @Override public long getObjectSize(IObjectSizes objectSizes, boolean doAlign) { long size = super.getObjectSize(objectSizes, doAlign); - size += objectSizes.getPrimitiveTypesSize(8, 0, 2, 0, 2, 3); + size += objectSizes.getPrimitiveTypesSize(9, 0, 2, 0, 2, 3); size += objectSizes.getSizeOf(timerData); size += objectSizes.getSizeOf(loggingData); size += objectSizes.getSizeOf(sqlStatementData); + size += objectSizes.getSizeOf(spanIdent); if (nestedSequences instanceof ArrayList) { size += objectSizes.getSizeOf(nestedSequences, 0); for (InvocationSequenceData invocationSequenceData : nestedSequences) { @@ -534,6 +570,7 @@ public long getObjectSize(IObjectSizes objectSizes, boolean doAlign) { public InvocationSequenceData getClonedInvocationSequence() { InvocationSequenceData clone = new InvocationSequenceData(this.getTimeStamp(), this.getPlatformIdent(), this.getSensorTypeIdent(), this.getMethodIdent()); clone.setId(this.getId()); + clone.setSpanIdent(this.getSpanIdent()); clone.setChildCount(this.getChildCount()); clone.setDuration(this.getDuration()); clone.setEnd(this.getEnd()); diff --git a/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/serializer/impl/SerializationManager.java b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/serializer/impl/SerializationManager.java index 2fe879e8c..a0cac705b 100644 --- a/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/serializer/impl/SerializationManager.java +++ b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/serializer/impl/SerializationManager.java @@ -132,6 +132,9 @@ import rocks.inspectit.shared.all.serializer.ISerializer; import rocks.inspectit.shared.all.serializer.SerializationException; import rocks.inspectit.shared.all.serializer.schema.ClassSchemaManager; +import rocks.inspectit.shared.all.tracing.data.ClientSpan; +import rocks.inspectit.shared.all.tracing.data.ServerSpan; +import rocks.inspectit.shared.all.tracing.data.SpanIdent; import rocks.inspectit.shared.all.util.IHibernateUtil; import rocks.inspectit.shared.all.util.KryoNetNetwork; import rocks.inspectit.shared.all.util.TimeFrame; @@ -395,6 +398,11 @@ protected InvocationTargetException create(Kryo kryo, Input input, Class(kryo, SubstitutionDescriptor.class)); + + // added with INSPECTIT-1921 + kryo.register(SpanIdent.class, new CustomCompatibleFieldSerializer(kryo, SpanIdent.class, schemaManager)); + kryo.register(ClientSpan.class, new CustomCompatibleFieldSerializer(kryo, ClientSpan.class, schemaManager, true)); + kryo.register(ServerSpan.class, new CustomCompatibleFieldSerializer(kryo, ServerSpan.class, schemaManager, true)); } /** diff --git a/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/constants/ExtraTags.java b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/constants/ExtraTags.java new file mode 100644 index 000000000..3d0cc5629 --- /dev/null +++ b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/constants/ExtraTags.java @@ -0,0 +1,35 @@ +package rocks.inspectit.shared.all.tracing.constants; + +/** + * Extra tags key that are not defined by the opentracing and used exclusively by inspectIT to + * enrich the span information. + *

+ * Not using the opentracing.io Tag implementation due to the default package modifiers. + * + * @author Ivan Senic + * + */ +public interface ExtraTags { + + /** + * Name for the propagation type tag we are using. + */ + String PROPAGATION_TYPE = "ext.propagation.type"; + + /** + * Operation name. As op name is used only in user spans, we will have this in our spans as a + * tag. + */ + String OPERATION_NAME = "ext.operation.name"; + + /** + * Jms message id tag. + */ + String JMS_MESSAGE_ID = "ext.jms.id"; + + /** + * Jms message destination tag. + */ + String JMS_MESSAGE_DESTINATION = "ext.jms.destination"; + +} diff --git a/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/AbstractSpan.java b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/AbstractSpan.java new file mode 100644 index 000000000..b5d0b1886 --- /dev/null +++ b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/AbstractSpan.java @@ -0,0 +1,284 @@ +package rocks.inspectit.shared.all.tracing.data; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import rocks.inspectit.shared.all.cmr.cache.IObjectSizes; +import rocks.inspectit.shared.all.communication.MethodSensorData; + +/** + * Base span. This span holds all information that all other types of spans do. + *

+ * The relationship in the span can be described in two ways: + *

    + *
  • 1. The caller or called attribute. Reference span can either call another reference span or + * be called. + *
  • 2. Relationship type describes what are time constraints between two spans. Can be Child-Of + * (caller wait for callee) or Follow-From (caller does not wait for the callee). More info or the + * relationships can be read in the class {@link ReferenceType}. + *
+ *

+ * We also capture the {@link PropagationType} that provides information if propagation was inter- + * or cross-process and what technology was used (for example HTTP). + * + * @author Ivan Senic + * + */ +public abstract class AbstractSpan extends MethodSensorData implements Span { + + /** + * Generated UID. + */ + private static final long serialVersionUID = -8144017430549409409L; + + /** + * Identifier of this span. + */ + private SpanIdent spanIdent; + + /** + * Duration of this span in milliseconds. We keep same resolution here as for the duration of + * our other monitoring data. + */ + private double duration; + + /** + * Propagation type. + * + * @see PropagationType + */ + private PropagationType propagationType; + + /** + * Reference type. + */ + private String referenceType; + + /** + * Defined tags. + */ + private Map tags; + + /** + * {@inheritDoc} + */ + @Override + public abstract boolean isCaller(); + + /** + * Gets {@link #spanIdent}. + * + * @return {@link #spanIdent} + */ + @Override + public SpanIdent getSpanIdent() { + return this.spanIdent; + } + + /** + * Sets {@link #spanIdent}. + * + * @param spanIdent + * New value for {@link #spanIdent} + */ + public void setSpanIdent(SpanIdent spanIdent) { + this.spanIdent = spanIdent; + } + + /** + * Gets {@link #duration}. + * + * @return {@link #duration} + */ + @Override + public double getDuration() { + return this.duration; + } + + /** + * Sets {@link #duration}. + * + * @param duration + * New value for {@link #duration} + */ + public void setDuration(double duration) { + this.duration = duration; + } + + /** + * Gets {@link #propagationType}. + * + * @return {@link #propagationType} + */ + @Override + public PropagationType getPropagationType() { + return this.propagationType; + } + + /** + * Sets {@link #propagationType}. + * + * @param propagationType + * New value for {@link #propagationType} + */ + public void setPropagationType(PropagationType propagationType) { + this.propagationType = propagationType; + } + + /** + * {@inheritDoc} + */ + @Override + public String getReferenceType() { + return referenceType; + } + + /** + * Sets {@link #ReferenceType}. + * + * @param referenceType + * New value for {@link #ReferenceType} + */ + public void setReferenceType(String referenceType) { + this.referenceType = referenceType; + } + + /** + * Adds tag to this span. + * + * @param tag + * {@link Tag}, must not be null. + * @param value + * String value, must not be null. + * @return Old value associated with same tag. + */ + @Override + public String addTag(String tag, String value) { + if (null == tags) { + tags = new HashMap(1, 1f); + } + return tags.put(tag, value); + } + + /** + * Adds all tags from the given map to the tags of this span. + * + * @param otherTags + * Map of tags to add. + */ + @Override + public void addAllTags(Map otherTags) { + if (null == tags) { + tags = new HashMap(otherTags.size(), 1f); + } + tags.putAll(otherTags); + } + + /** + * {@inheritDoc} + */ + @Override + public Map getTags() { + if (null == tags) { + return Collections.emptyMap(); + } else { + return Collections.unmodifiableMap(tags); + } + } + + /** + * {@inheritDoc} + */ + @Override + public long getObjectSize(IObjectSizes objectSizes, boolean doAlign) { + long size = super.getObjectSize(objectSizes, doAlign); + size += objectSizes.getPrimitiveTypesSize(4, 0, 0, 0, 0, 1); + size += objectSizes.getSizeOf(spanIdent); + if (null != tags) { + int tagsSize = tags.size(); + size += objectSizes.getSizeOfHashMap(tagsSize); + size += tagsSize + objectSizes.getSizeOfIntegerObject(); + size += objectSizes.getSizeOf(tags.values().toArray(new String[tagsSize])); + } + + if (doAlign) { + return objectSizes.alignTo8Bytes(size); + } else { + return size; + } + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + long temp; + temp = Double.doubleToLongBits(this.duration); + result = (prime * result) + (int) (temp ^ (temp >>> 32)); + result = (prime * result) + ((this.propagationType == null) ? 0 : this.propagationType.hashCode()); + result = (prime * result) + ((this.referenceType == null) ? 0 : this.referenceType.hashCode()); + result = (prime * result) + ((this.spanIdent == null) ? 0 : this.spanIdent.hashCode()); + result = (prime * result) + ((this.tags == null) ? 0 : this.tags.hashCode()); + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + AbstractSpan other = (AbstractSpan) obj; + if (Double.doubleToLongBits(this.duration) != Double.doubleToLongBits(other.duration)) { + return false; + } + if (this.propagationType != other.propagationType) { + return false; + } + if (this.referenceType == null) { + if (other.referenceType != null) { + return false; + } + } else if (!this.referenceType.equals(other.referenceType)) { + return false; + } + if (this.spanIdent == null) { + if (other.spanIdent != null) { + return false; + } + } else if (!this.spanIdent.equals(other.spanIdent)) { + return false; + } + if (this.tags == null) { + if (other.tags != null) { + return false; + } + } else if (!this.tags.equals(other.tags)) { + return false; + } + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "AbstractSpan [getSpanIdent()=" + this.getSpanIdent() + ", getPropagationType()=" + this.getPropagationType() + ", getReferenceType()=" + this.getReferenceType() + ", isCaller()=" + + this.isCaller() + ", getTags()=" + this.getTags() + ", getDuration()=" + this.getDuration() + "]"; + } + + +} diff --git a/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/ClientSpan.java b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/ClientSpan.java new file mode 100644 index 000000000..ecd0c96d3 --- /dev/null +++ b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/ClientSpan.java @@ -0,0 +1,26 @@ +package rocks.inspectit.shared.all.tracing.data; + +/** + * The client span is the span that makes a call usually over RPC to another span. Currently it's + * the only difference to a server span, but in future we might add different behavior methods, thus + * these two are separated. + * + * @author Ivan Senic + * + */ +public class ClientSpan extends AbstractSpan { + + /** + * Generated UID. + */ + private static final long serialVersionUID = -5415646396600896897L; + + /** + * {@inheritDoc} + */ + @Override + public boolean isCaller() { + return true; + } + +} diff --git a/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/PropagationType.java b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/PropagationType.java new file mode 100644 index 000000000..74365ed0b --- /dev/null +++ b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/PropagationType.java @@ -0,0 +1,42 @@ +package rocks.inspectit.shared.all.tracing.data; + +/** + * This enum defines how reference spans are propagated related to cross-process propagation. + * + * @author Ivan Senic + * + */ +public enum PropagationType { + + /** + * Process propagation. Means that reference spans are executed in the same process and + * inter-process propagation was used. + */ + PROCESS, + + /** + * Propagation via HTTP or HTTPS. + */ + HTTP, + + /** + * Propagation via Java Message Service. + */ + JMS; + + /** + * Returns result of {@link #valueOf(String)} if the given parameter is not null. + * + * @param propagation + * as string + * @return {@link PropagationType} enum or null + */ + public static PropagationType safeValueOf(String propagation) { + if (null != propagation) { + return valueOf(propagation); + } else { + return null; + } + } + +} diff --git a/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/ServerSpan.java b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/ServerSpan.java new file mode 100644 index 000000000..90dcf2020 --- /dev/null +++ b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/ServerSpan.java @@ -0,0 +1,26 @@ +package rocks.inspectit.shared.all.tracing.data; + +/** + * The server span is the span that is usually called over RPC or simply created manually. Currently + * it's the only difference to a client span, but in future we might add different behavior methods, + * thus these two are separated. + * + * @author Ivan Senic + * + */ +public class ServerSpan extends AbstractSpan { + + /** + * Generated UID. + */ + private static final long serialVersionUID = 7430959547274725654L; + + /** + * {@inheritDoc} + */ + @Override + public boolean isCaller() { + return false; + } + +} diff --git a/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/Span.java b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/Span.java new file mode 100644 index 000000000..86d7f6bba --- /dev/null +++ b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/Span.java @@ -0,0 +1,85 @@ +package rocks.inspectit.shared.all.tracing.data; + +import java.sql.Timestamp; +import java.util.Map; + +/** + * Interface for all the spans that we have. + * + * @author Ivan Senic + * + */ +public interface Span { + + /** + * Returns the span identification. Must not be null. + * + * @return Returns the span identification. Must not be null. + */ + SpanIdent getSpanIdent(); + + /** + * Time-stamp when the span started. + * + * @return Time-stamp when the span started. + */ + Timestamp getTimeStamp(); + + /** + * Duration of the span in milliseconds. + * + * @return Duration of the span in milliseconds. + */ + double getDuration(); + + /** + * Adds tag to this span. + * + * @param tag + * {@link String}, must not be null. + * @param value + * String value, must not be null. + * @return Old value associated with same tag. + */ + String addTag(String tag, String value); + + /** + * Adds all tags from the given map to the tags of this span. + * + * @param otherTags + * Map of tags to add. + */ + void addAllTags(Map otherTags); + + /** + * Tags that are giving more information about the span. + * + * @return Tags that are giving more information about the span. + */ + Map getTags(); + + /** + * Returns propagation type that defines how reference spans are propagated related to + * cross-process propagation. + * + * @return {@link PropagationType} + */ + PropagationType getPropagationType(); + + /** + * Reference type describes what are time constraints between two spans. Can be Child-Of (caller + * wait for callee) or Follow-From (caller does not wait for the callee). More info or the + * relationships can be read in the class {@link ReferenceType}. + * + * @return {@link ReferenceType} + */ + String getReferenceType(); + + /** + * Denotes if the this span is caller span. + * + * @return If it is caller span. + */ + boolean isCaller(); + +} diff --git a/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/SpanIdent.java b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/SpanIdent.java new file mode 100644 index 000000000..4606dd9b6 --- /dev/null +++ b/inspectit.shared.all/src/main/java/rocks/inspectit/shared/all/tracing/data/SpanIdent.java @@ -0,0 +1,168 @@ +package rocks.inspectit.shared.all.tracing.data; + +import java.io.Serializable; + +import rocks.inspectit.shared.all.cmr.cache.IObjectSizes; +import rocks.inspectit.shared.all.communication.Sizeable; + +/** + * Holds identification for any span. Each span identification has unique ID. There are also trace + * and parent ID fields. Span is considered to be root if both parent and trace IDs are same as the + * span ID. + * + * @author Ivan Senic + * + */ +public class SpanIdent implements Sizeable, Serializable { + + /** + * Generated UID. + */ + private static final long serialVersionUID = -4529002759213057498L; + + /** + * Unique ID of the span. + */ + private final long id; + + /** + * ID of the trace that span belongs to. Can be same as {@link #id} when it's a root span. + */ + private final long traceId; + + /** + * ID of the span's parent. Can be same as {@link #id} to denote that there is no parent. + */ + private final long parentId; + + /** + * No-arg constructor. + */ + protected SpanIdent() { + this(0, 0, 0); + } + + /** + * Default constructor. + * + * @param id + * Unique ID of the span. + * @param traceId + * ID of the trace that span belongs to. Can be same as {@link #id}. + * @param parentId + * ID of the span's parent. Can be 0 to denote that there is no parent. + */ + public SpanIdent(long id, long traceId, long parentId) { + this.id = id; + this.traceId = traceId; + this.parentId = parentId; + } + + /** + * If this is span identification for a root span. + * + * @return If this is span identification for a root span. + */ + public boolean isRoot() { + return (this.parentId == this.id) && (this.traceId == this.id); + } + + /** + * 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 long getObjectSize(IObjectSizes objectSizes) { + return getObjectSize(objectSizes, true); + } + + /** + * {@inheritDoc} + */ + @Override + public long getObjectSize(IObjectSizes objectSizes, boolean doAlign) { + long size = objectSizes.getSizeOfObjectHeader(); + size += objectSizes.getPrimitiveTypesSize(0, 0, 0, 0, 3, 0); + + if (doAlign) { + return objectSizes.alignTo8Bytes(size); + } else { + return size; + } + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (prime * result) + (int) (this.id ^ (this.id >>> 32)); + result = (prime * result) + (int) (this.parentId ^ (this.parentId >>> 32)); + 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; + } + SpanIdent other = (SpanIdent) obj; + if (this.id != other.id) { + return false; + } + if (this.parentId != other.parentId) { + return false; + } + if (this.traceId != other.traceId) { + return false; + } + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "SpanIdent [id=" + this.id + ", traceId=" + this.traceId + ", parentId=" + this.parentId + ", isRoot()=" + this.isRoot() + "]"; + } + +} diff --git a/inspectit.shared.all/src/main/resources/META-INF/MANIFEST.MF b/inspectit.shared.all/src/main/resources/META-INF/MANIFEST.MF index a97bc7d8f..ebb898828 100644 --- a/inspectit.shared.all/src/main/resources/META-INF/MANIFEST.MF +++ b/inspectit.shared.all/src/main/resources/META-INF/MANIFEST.MF @@ -38,6 +38,9 @@ Export-Package: rocks.inspectit.shared.all.cmr.cache, rocks.inspectit.shared.all.storage.nio.bytebuffer, rocks.inspectit.shared.all.storage.nio.stream, rocks.inspectit.shared.all.testbase, + rocks.inspectit.shared.all.tracing.constants, + rocks.inspectit.shared.all.tracing.data, + rocks.inspectit.shared.all.tracing.util, rocks.inspectit.shared.all.util, rocks.inspectit.shared.all.version, rocks.inspectit.shared.all.versioning, diff --git a/inspectit.shared.all/src/main/resources/schema/AbstractSpan.sch b/inspectit.shared.all/src/main/resources/schema/AbstractSpan.sch new file mode 100644 index 000000000..8acc8bb93 --- /dev/null +++ b/inspectit.shared.all/src/main/resources/schema/AbstractSpan.sch @@ -0,0 +1,18 @@ +class: rocks.inspectit.shared.all.tracing.data.AbstractSpan + +# Default Data +1: id +2: platformIdent +3: sensorTypeIdent +4: timeStamp + +# Method Sensor Data +5: methodIdent +6: parameterContentData + +# Abstract Span +7: spanIdent +8: duration +9: propagationType +10: referenceType +11: tags diff --git a/inspectit.shared.all/src/main/resources/schema/InvocationSequenceData.sch b/inspectit.shared.all/src/main/resources/schema/InvocationSequenceData.sch index 3e9986a2f..9658a3df6 100644 --- a/inspectit.shared.all/src/main/resources/schema/InvocationSequenceData.sch +++ b/inspectit.shared.all/src/main/resources/schema/InvocationSequenceData.sch @@ -27,3 +27,4 @@ class: rocks.inspectit.shared.all.communication.data.InvocationSequenceData 18: loggingData 19: applicationId 20: businessTransactionId +21: spanIdent diff --git a/inspectit.shared.all/src/main/resources/schema/SpanIdent.sch b/inspectit.shared.all/src/main/resources/schema/SpanIdent.sch new file mode 100644 index 000000000..1b56971d0 --- /dev/null +++ b/inspectit.shared.all/src/main/resources/schema/SpanIdent.sch @@ -0,0 +1,6 @@ +class: rocks.inspectit.shared.all.tracing.data.SpanIdent + +# SpanIdent +1: id +2: traceId +3: parentId \ No newline at end of file diff --git a/inspectit.shared.all/src/main/resources/schema/schemaList.txt b/inspectit.shared.all/src/main/resources/schema/schemaList.txt index 880f992ce..df845e73c 100644 --- a/inspectit.shared.all/src/main/resources/schema/schemaList.txt +++ b/inspectit.shared.all/src/main/resources/schema/schemaList.txt @@ -42,4 +42,6 @@ schema/JmxDefinitionDataIdent.sch schema/JmxSensorValueData.sch schema/HttpInfo.sch schema/ApplicationData.sch -schema/BusinessTransactionData.sch \ No newline at end of file +schema/BusinessTransactionData.sch +schema/SpanIdent.sch +schema/AbstractSpan.sch \ No newline at end of file diff --git a/inspectit.shared.all/src/test/java/rocks/inspectit/shared/all/cmr/cache/impl/ObjectSizesPrimitiveTypesSizeTest.java b/inspectit.shared.all/src/test/java/rocks/inspectit/shared/all/cmr/cache/impl/ObjectSizesPrimitiveTypesSizeTest.java index 457d25ac0..562d563f6 100644 --- a/inspectit.shared.all/src/test/java/rocks/inspectit/shared/all/cmr/cache/impl/ObjectSizesPrimitiveTypesSizeTest.java +++ b/inspectit.shared.all/src/test/java/rocks/inspectit/shared/all/cmr/cache/impl/ObjectSizesPrimitiveTypesSizeTest.java @@ -39,6 +39,8 @@ import rocks.inspectit.shared.all.communication.data.ThreadInformationData; import rocks.inspectit.shared.all.communication.data.TimerData; import rocks.inspectit.shared.all.communication.data.VmArgumentData; +import rocks.inspectit.shared.all.tracing.data.ClientSpan; +import rocks.inspectit.shared.all.tracing.data.ServerSpan; /** * This tests checks all the {@link DefaultData} classes for the proper use of the @@ -57,7 +59,7 @@ public class ObjectSizesPrimitiveTypesSizeTest { { SqlStatementData.class }, { ExceptionSensorData.class }, { InvocationSequenceData.class }, { ClassLoadingInformationData.class }, { CompilationInformationData.class }, { MemoryInformationData.class }, { RuntimeInformationData.class }, { SystemInformationData.class }, { ThreadInformationData.class }, { HttpTimerData.class }, { AggregatedExceptionSensorData.class }, { AggregatedHttpTimerData.class }, { AggregatedSqlStatementData.class }, { AggregatedTimerData.class }, { ParameterContentData.class }, - { HttpInfo.class }, { VmArgumentData.class } }; + { HttpInfo.class }, { VmArgumentData.class }, { ServerSpan.class }, { ClientSpan.class } }; /** * Enums that implement sizable. diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/Environment.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/Environment.java index 002323e00..46267cf4d 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/Environment.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/Environment.java @@ -17,6 +17,7 @@ import rocks.inspectit.shared.cs.ci.sensor.exception.IExceptionSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.exception.impl.ExceptionSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.jmx.JmxSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.AbstractRemoteSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.IMethodSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.ConnectionSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.HttpSensorConfig; @@ -26,14 +27,8 @@ import rocks.inspectit.shared.cs.ci.sensor.method.impl.PreparedStatementSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.StatementSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.TimerSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.platform.AbstractPlatformSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.platform.IPlatformSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.ClassLoadingSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.CompilationSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.CpuSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.MemorySensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.RuntimeSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.SystemSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.ThreadSensorConfig; import rocks.inspectit.shared.cs.ci.strategy.IStrategyConfig; import rocks.inspectit.shared.cs.ci.strategy.impl.ListSendingStrategyConfig; import rocks.inspectit.shared.cs.ci.strategy.impl.SimpleBufferStrategyConfig; @@ -72,9 +67,7 @@ public class Environment extends AbstractCiData { * List of the platform sensors configurations. */ @XmlElementWrapper(name = "platform-sensor-configs") - @XmlElementRefs({ @XmlElementRef(type = ClassLoadingSensorConfig.class), @XmlElementRef(type = CompilationSensorConfig.class), @XmlElementRef(type = CpuSensorConfig.class), - @XmlElementRef(type = MemorySensorConfig.class), @XmlElementRef(type = RuntimeSensorConfig.class), @XmlElementRef(type = SystemSensorConfig.class), - @XmlElementRef(type = ThreadSensorConfig.class) }) + @XmlElementRefs({ @XmlElementRef(type = AbstractPlatformSensorConfig.class) }) private final List platformSensorConfigs = ConfigurationDefaultsFactory.getAvailablePlatformSensorConfigs(); /** @@ -83,7 +76,7 @@ public class Environment extends AbstractCiData { @XmlElementWrapper(name = "method-sensor-configs") @XmlElementRefs({ @XmlElementRef(type = ConnectionSensorConfig.class), @XmlElementRef(type = HttpSensorConfig.class), @XmlElementRef(type = InvocationSequenceSensorConfig.class), @XmlElementRef(type = PreparedStatementParameterSensorConfig.class), @XmlElementRef(type = PreparedStatementSensorConfig.class), @XmlElementRef(type = StatementSensorConfig.class), - @XmlElementRef(type = TimerSensorConfig.class), @XmlElementRef(type = Log4jLoggingSensorConfig.class) }) + @XmlElementRef(type = TimerSensorConfig.class), @XmlElementRef(type = Log4jLoggingSensorConfig.class), @XmlElementRef(type = AbstractRemoteSensorConfig.class) }) private final List methodSensorConfigs = ConfigurationDefaultsFactory.getAvailableMethodSensorConfigs(); /** diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/factory/ConfigurationDefaultsFactory.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/factory/ConfigurationDefaultsFactory.java index 288a17e47..9052c92d6 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/factory/ConfigurationDefaultsFactory.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/factory/ConfigurationDefaultsFactory.java @@ -17,6 +17,14 @@ import rocks.inspectit.shared.cs.ci.sensor.method.impl.Log4jLoggingSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.PreparedStatementParameterSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.PreparedStatementSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteApacheHttpClientV40SensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJavaHttpServerSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJettyHttpClientV61ClientSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJmsClientSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJmsListenerServerSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteManualServerSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteSpringRestTemplateClientSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteUrlConnectionClientSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.StatementSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.TimerSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.platform.IPlatformSensorConfig; @@ -96,6 +104,14 @@ public static List getAvailableMethodSensorConfigs() { methodSensorConfigs.add(new StatementSensorConfig()); methodSensorConfigs.add(new TimerSensorConfig()); methodSensorConfigs.add(new Log4jLoggingSensorConfig()); + methodSensorConfigs.add(new RemoteApacheHttpClientV40SensorConfig()); + methodSensorConfigs.add(new RemoteJettyHttpClientV61ClientSensorConfig()); + methodSensorConfigs.add(new RemoteUrlConnectionClientSensorConfig()); + methodSensorConfigs.add(new RemoteSpringRestTemplateClientSensorConfig()); + methodSensorConfigs.add(new RemoteJavaHttpServerSensorConfig()); + methodSensorConfigs.add(new RemoteJmsClientSensorConfig()); + methodSensorConfigs.add(new RemoteJmsListenerServerSensorConfig()); + methodSensorConfigs.add(new RemoteManualServerSensorConfig()); return methodSensorConfigs; } diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/ISensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/ISensorConfig.java index a30b5aea3..748252b9a 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/ISensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/ISensorConfig.java @@ -10,6 +10,13 @@ */ public interface ISensorConfig { + /** + * Returns sensor name. + * + * @return Returns sensor name. + */ + String getName(); + /** * Returns the class name of the sensor type as fully qualified. * diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/exception/impl/ExceptionSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/exception/impl/ExceptionSensorConfig.java index 7fdee4319..497341011 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/exception/impl/ExceptionSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/exception/impl/ExceptionSensorConfig.java @@ -22,7 +22,7 @@ public class ExceptionSensorConfig extends StringConstraintSensorConfig implemen /** * Sensor name. */ - private static final String SENSOR_NAME = "Exception Sensor"; + public static final String SENSOR_NAME = "Exception Sensor"; /** * Implementing class name. diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/jmx/JmxSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/jmx/JmxSensorConfig.java index a09f94b4c..9daf136ff 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/jmx/JmxSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/jmx/JmxSensorConfig.java @@ -20,6 +20,11 @@ @XmlAccessorType(XmlAccessType.FIELD) public class JmxSensorConfig implements ISensorConfig { + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "JMX Sensor"; + /** * Implementing class name. */ @@ -41,6 +46,14 @@ public class JmxSensorConfig implements ISensorConfig { @XmlAttribute(name = "forceMBeanServerCreation") private boolean forceMBeanServer = false; + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + /** * {@inheritDoc} */ diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/AbstractRemoteSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/AbstractRemoteSensorConfig.java new file mode 100644 index 000000000..0c56df03c --- /dev/null +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/AbstractRemoteSensorConfig.java @@ -0,0 +1,80 @@ +package rocks.inspectit.shared.cs.ci.sensor.method; + +import java.util.Collections; +import java.util.Map; + +import javax.xml.bind.annotation.XmlSeeAlso; + +import rocks.inspectit.shared.all.instrumentation.config.PriorityEnum; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteApacheHttpClientV40SensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJavaHttpServerSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJettyHttpClientV61ClientSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJmsClientSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJmsListenerServerSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteManualServerSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteSpringRestTemplateClientSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteUrlConnectionClientSensorConfig; + +/** + * Abstract class for all remote sensor configurations. + * + * @author Thomas Kluge + * + */ +@XmlSeeAlso({ RemoteJavaHttpServerSensorConfig.class, RemoteJmsListenerServerSensorConfig.class, RemoteApacheHttpClientV40SensorConfig.class, RemoteUrlConnectionClientSensorConfig.class, + RemoteJettyHttpClientV61ClientSensorConfig.class, RemoteSpringRestTemplateClientSensorConfig.class, RemoteJmsClientSensorConfig.class, RemoteManualServerSensorConfig.class }) +public abstract class AbstractRemoteSensorConfig implements IMethodSensorConfig { + + /** + * {@inheritDoc} + */ + @Override + public PriorityEnum getPriority() { + return PriorityEnum.NORMAL; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAdvanced() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public Map getParameters() { + return Collections.emptyMap(); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (prime * result) + this.getClass().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; + } + return true; + } + +} diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/ConnectionSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/ConnectionSensorConfig.java index 50030189a..6f7f521f3 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/ConnectionSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/ConnectionSensorConfig.java @@ -18,7 +18,7 @@ public class ConnectionSensorConfig extends AbstractMethodSensorConfig implement /** * Sensor name. */ - private static final String SENSOR_NAME = "JDBC Connection Sensor"; + public static final String SENSOR_NAME = "JDBC Connection Sensor"; /** * Implementing class name. diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/HttpSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/HttpSensorConfig.java index 33075e1be..5d7ae2215 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/HttpSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/HttpSensorConfig.java @@ -21,7 +21,7 @@ public class HttpSensorConfig extends StringConstraintSensorConfig implements IM /** * Sensor name. */ - private static final String SENSOR_NAME = "HTTP Sensor"; + public static final String SENSOR_NAME = "HTTP Sensor"; /** * Implementing class name. diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/InvocationSequenceSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/InvocationSequenceSensorConfig.java index 5e8b51ddb..3101ab59e 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/InvocationSequenceSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/InvocationSequenceSensorConfig.java @@ -18,7 +18,7 @@ public class InvocationSequenceSensorConfig extends StringConstraintSensorConfig /** * Sensor name. */ - private static final String SENSOR_NAME = "Invocation Sequence Sensor"; + public static final String SENSOR_NAME = "Invocation Sequence Sensor"; /** * Implementing class name. diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/Log4jLoggingSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/Log4jLoggingSensorConfig.java index e4a887e4e..4c4ef2c5b 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/Log4jLoggingSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/Log4jLoggingSensorConfig.java @@ -27,7 +27,7 @@ public class Log4jLoggingSensorConfig extends AbstractMethodSensorConfig impleme /** * Sensor name. */ - private static final String SENSOR_NAME = "Logging Sensor for log4j"; + public static final String SENSOR_NAME = "Logging Sensor for log4j"; /** * Implementing class name. diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/PreparedStatementParameterSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/PreparedStatementParameterSensorConfig.java index e4544b8f9..f71da7fb0 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/PreparedStatementParameterSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/PreparedStatementParameterSensorConfig.java @@ -18,7 +18,7 @@ public class PreparedStatementParameterSensorConfig extends AbstractMethodSensor /** * Sensor name. */ - private static final String SENSOR_NAME = "JDBC Prepared Statement Parameter Sensor"; + public static final String SENSOR_NAME = "JDBC Prepared Statement Parameter Sensor"; /** * Implementing class name. diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/PreparedStatementSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/PreparedStatementSensorConfig.java index 17bfe6ee4..2c43ddf17 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/PreparedStatementSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/PreparedStatementSensorConfig.java @@ -18,7 +18,7 @@ public class PreparedStatementSensorConfig extends StringConstraintSensorConfig /** * Sensor name. */ - private static final String SENSOR_NAME = "JDBC Prepared Statement Sensor"; + public static final String SENSOR_NAME = "JDBC Prepared Statement Sensor"; /** * Implementing class name. diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteApacheHttpClientV40SensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteApacheHttpClientV40SensorConfig.java new file mode 100644 index 000000000..424dde0fa --- /dev/null +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteApacheHttpClientV40SensorConfig.java @@ -0,0 +1,42 @@ +package rocks.inspectit.shared.cs.ci.sensor.method.impl; + +import javax.xml.bind.annotation.XmlRootElement; + +import rocks.inspectit.shared.cs.ci.sensor.method.AbstractRemoteSensorConfig; + +/** + * Remote Apache HttpClient client sensor config. + * + * @author Thomas Kluge + * + */ +@XmlRootElement(name = "remote-apache-httpclientV40-client-sensor-config") +public class RemoteApacheHttpClientV40SensorConfig extends AbstractRemoteSensorConfig { + + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "Remote Apache HTTP Client Sensor"; + + /** + * Implementing class name. + */ + public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.method.remote.client.http.ApacheHttpClientV40Sensor"; + + /** + * {@inheritDoc} + */ + @Override + public String getClassName() { + return CLASS_NAME; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + +} diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteJavaHttpServerSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteJavaHttpServerSensorConfig.java new file mode 100644 index 000000000..d0a6f3e7c --- /dev/null +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteJavaHttpServerSensorConfig.java @@ -0,0 +1,41 @@ +package rocks.inspectit.shared.cs.ci.sensor.method.impl; + +import javax.xml.bind.annotation.XmlRootElement; + +import rocks.inspectit.shared.cs.ci.sensor.method.AbstractRemoteSensorConfig; + +/** + * Remote HTTP java server sensor config. + * + * @author Thomas Kluge + * + */ +@XmlRootElement(name = "remote-http-server-sensor-config") +public class RemoteJavaHttpServerSensorConfig extends AbstractRemoteSensorConfig { + + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "Remote Java Http Server Sensor"; + + /** + * Implementing class name. + */ + public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.method.remote.server.http.JavaHttpRemoteServerSensor"; + + /** + * {@inheritDoc} + */ + @Override + public String getClassName() { + return CLASS_NAME; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } +} diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteJettyHttpClientV61ClientSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteJettyHttpClientV61ClientSensorConfig.java new file mode 100644 index 000000000..072485b2f --- /dev/null +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteJettyHttpClientV61ClientSensorConfig.java @@ -0,0 +1,42 @@ +package rocks.inspectit.shared.cs.ci.sensor.method.impl; + +import javax.xml.bind.annotation.XmlRootElement; + +import rocks.inspectit.shared.cs.ci.sensor.method.AbstractRemoteSensorConfig; + +/** + * Remote Jetty HttpClient client sensor config. + * + * @author Thomas Kluge + * + */ +@XmlRootElement(name = "remote-jetty-httpclientV61-client-sensor-config") +public class RemoteJettyHttpClientV61ClientSensorConfig extends AbstractRemoteSensorConfig { + + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "Remote Jetty HTTP Client Sensor"; + + /** + * Implementing class name. + */ + public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.method.remote.client.http.JettyHttpClientV61Sensor"; + + /** + * {@inheritDoc} + */ + @Override + public String getClassName() { + return CLASS_NAME; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + +} diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteJmsClientSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteJmsClientSensorConfig.java new file mode 100644 index 000000000..7de52a1ad --- /dev/null +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteJmsClientSensorConfig.java @@ -0,0 +1,42 @@ +package rocks.inspectit.shared.cs.ci.sensor.method.impl; + +import javax.xml.bind.annotation.XmlRootElement; + +import rocks.inspectit.shared.cs.ci.sensor.method.AbstractRemoteSensorConfig; + +/** + * Remote MQ client sensor. + * + * @author Thomas Kluge + * + */ +@XmlRootElement(name = "remote-jms-client-sensor-config") +public class RemoteJmsClientSensorConfig extends AbstractRemoteSensorConfig { + + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "Remote JMS Client Sensor"; + + /** + * Implementing class name. + */ + public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.method.remote.client.mq.JmsRemoteClientSensor"; + + /** + * {@inheritDoc} + */ + @Override + public String getClassName() { + return CLASS_NAME; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + +} diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteJmsListenerServerSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteJmsListenerServerSensorConfig.java new file mode 100644 index 000000000..d85656607 --- /dev/null +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteJmsListenerServerSensorConfig.java @@ -0,0 +1,41 @@ +package rocks.inspectit.shared.cs.ci.sensor.method.impl; + +import javax.xml.bind.annotation.XmlRootElement; + +import rocks.inspectit.shared.cs.ci.sensor.method.AbstractRemoteSensorConfig; + +/** + * Remote MQ Listener server sensor config. + * + * @author Thomas Kluge + * + */ +@XmlRootElement(name = "remote-jms-listener-server-sensor-config") +public class RemoteJmsListenerServerSensorConfig extends AbstractRemoteSensorConfig { + + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "Remote JMS Listener Server Sensor"; + + /** + * Implementing class name. + */ + public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.method.remote.server.mq.JmsListenerRemoteServerSensor"; + + /** + * {@inheritDoc} + */ + @Override + public String getClassName() { + return CLASS_NAME; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } +} diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteManualServerSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteManualServerSensorConfig.java new file mode 100644 index 000000000..2916f4859 --- /dev/null +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteManualServerSensorConfig.java @@ -0,0 +1,50 @@ +package rocks.inspectit.shared.cs.ci.sensor.method.impl; + +import javax.xml.bind.annotation.XmlRootElement; + +import rocks.inspectit.shared.cs.ci.sensor.method.AbstractRemoteSensorConfig; + +/** + * Remote manual serner sensor config. + * + * @author Ivan Senic + * + */ +@XmlRootElement(name = "remote-manual-server-sensor-config") +public class RemoteManualServerSensorConfig extends AbstractRemoteSensorConfig { + + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "Remote Manual Server Sensor"; + + /** + * Implementing class name. + */ + public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.method.remote.server.manual.ManualRemoteServerSensor"; + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + + /** + * {@inheritDoc} + */ + @Override + public String getClassName() { + return CLASS_NAME; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAdvanced() { + return false; + } + +} diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteSpringRestTemplateClientSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteSpringRestTemplateClientSensorConfig.java new file mode 100644 index 000000000..8ad30744b --- /dev/null +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteSpringRestTemplateClientSensorConfig.java @@ -0,0 +1,42 @@ +package rocks.inspectit.shared.cs.ci.sensor.method.impl; + +import javax.xml.bind.annotation.XmlRootElement; + +import rocks.inspectit.shared.cs.ci.sensor.method.AbstractRemoteSensorConfig; + +/** + * Remote Spring REST Template sensor config. + * + * @author Ivan Senic + * + */ +@XmlRootElement(name = "remote-spring-resttemplate-client-sensor-config") +public class RemoteSpringRestTemplateClientSensorConfig extends AbstractRemoteSensorConfig { + + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "Remote Spring RestTemplate Client Sensor"; + + /** + * Implementing class name. + */ + public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.method.remote.client.http.SpringRestTemplateClientSensor"; + + /** + * {@inheritDoc} + */ + @Override + public String getClassName() { + return CLASS_NAME; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + +} diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteUrlConnectionClientSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteUrlConnectionClientSensorConfig.java new file mode 100644 index 000000000..1ade8fad1 --- /dev/null +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/RemoteUrlConnectionClientSensorConfig.java @@ -0,0 +1,42 @@ +package rocks.inspectit.shared.cs.ci.sensor.method.impl; + +import javax.xml.bind.annotation.XmlRootElement; + +import rocks.inspectit.shared.cs.ci.sensor.method.AbstractRemoteSensorConfig; + +/** + * Remote Http UrlConnection client sensor config. + * + * @author Thomas Kluge + * + */ +@XmlRootElement(name = "remote-urlconnection-client-sensor-config") +public class RemoteUrlConnectionClientSensorConfig extends AbstractRemoteSensorConfig { + + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "Remote URL Connection Client Sensor"; + + /** + * Implementing class name. + */ + public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.method.remote.client.http.UrlConnectionSensor"; + + /** + * {@inheritDoc} + */ + @Override + public String getClassName() { + return CLASS_NAME; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + +} diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/StatementSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/StatementSensorConfig.java index 6249901ec..8244e2566 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/StatementSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/StatementSensorConfig.java @@ -18,7 +18,7 @@ public class StatementSensorConfig extends StringConstraintSensorConfig implemen /** * Sensor name. */ - private static final String SENSOR_NAME = "JDBC Statement Sensor"; + public static final String SENSOR_NAME = "JDBC Statement Sensor"; /** * Implementing class name. diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/TimerSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/TimerSensorConfig.java index 963f33fc8..924e385b5 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/TimerSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/method/impl/TimerSensorConfig.java @@ -18,7 +18,7 @@ public class TimerSensorConfig extends StringConstraintSensorConfig implements I /** * Sensor name. */ - private static final String SENSOR_NAME = "Timer Sensor"; + public static final String SENSOR_NAME = "Timer Sensor"; /** * Implementing class name. diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/ClassLoadingSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/ClassLoadingSensorConfig.java index f87b98ca7..ce88fc587 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/ClassLoadingSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/ClassLoadingSensorConfig.java @@ -14,11 +14,24 @@ @XmlRootElement(name = "class-loading-sensor-config") public class ClassLoadingSensorConfig extends AbstractPlatformSensorConfig implements IPlatformSensorConfig { + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "Class Loading Information"; + /** * Implementing class name. */ public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.platform.ClassLoadingInformation"; + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + /** * {@inheritDoc} */ diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/CompilationSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/CompilationSensorConfig.java index 8895d1e68..c016efa56 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/CompilationSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/CompilationSensorConfig.java @@ -14,11 +14,24 @@ @XmlRootElement(name = "compilation-sensor-config") public class CompilationSensorConfig extends AbstractPlatformSensorConfig implements IPlatformSensorConfig { + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "Compilation Information"; + /** * Implementing class name. */ public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.platform.CompilationInformation"; + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + /** * {@inheritDoc} */ diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/CpuSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/CpuSensorConfig.java index 1628cdcb0..76c9f2f5e 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/CpuSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/CpuSensorConfig.java @@ -14,11 +14,24 @@ @XmlRootElement(name = "cpu-sensor-config") public class CpuSensorConfig extends AbstractPlatformSensorConfig implements IPlatformSensorConfig { + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "CPU Information"; + /** * Implementing class name. */ public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.platform.CpuInformation"; + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + /** * {@inheritDoc} */ diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/MemorySensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/MemorySensorConfig.java index 81904d17f..331b493bc 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/MemorySensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/MemorySensorConfig.java @@ -14,11 +14,24 @@ @XmlRootElement(name = "memory-sensor-config") public class MemorySensorConfig extends AbstractPlatformSensorConfig implements IPlatformSensorConfig { + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "Memory Information"; + /** * Implementing class name. */ public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.platform.MemoryInformation"; + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + /** * {@inheritDoc} */ diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/RuntimeSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/RuntimeSensorConfig.java index c5adbfa03..670e6f259 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/RuntimeSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/RuntimeSensorConfig.java @@ -14,11 +14,24 @@ @XmlRootElement(name = "runtime-sensor-config") public class RuntimeSensorConfig extends AbstractPlatformSensorConfig implements IPlatformSensorConfig { + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "Runtime Information"; + /** * Implementing class name. */ public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.platform.RuntimeInformation"; + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + /** * {@inheritDoc} */ diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/SystemSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/SystemSensorConfig.java index 6e1219baa..7d703d2d6 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/SystemSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/SystemSensorConfig.java @@ -14,11 +14,24 @@ @XmlRootElement(name = "system-sensor-config") public class SystemSensorConfig extends AbstractPlatformSensorConfig implements IPlatformSensorConfig { + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "System Information"; + /** * Implementing class name. */ public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.platform.SystemInformation"; + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + /** * {@inheritDoc} */ diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/ThreadSensorConfig.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/ThreadSensorConfig.java index c224c8038..74d7345e4 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/ThreadSensorConfig.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/ci/sensor/platform/impl/ThreadSensorConfig.java @@ -14,11 +14,24 @@ @XmlRootElement(name = "thread-sensor-config") public class ThreadSensorConfig extends AbstractPlatformSensorConfig implements IPlatformSensorConfig { + /** + * Sensor name. + */ + public static final String SENSOR_NAME = "Thread Information"; + /** * Implementing class name. */ public static final String CLASS_NAME = "rocks.inspectit.agent.java.sensor.platform.ThreadInformation"; + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return SENSOR_NAME; + } + /** * {@inheritDoc} */ diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/jaxb/ISchemaVersionAware.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/jaxb/ISchemaVersionAware.java index 55134bfa5..cd13a7b8c 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/jaxb/ISchemaVersionAware.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/jaxb/ISchemaVersionAware.java @@ -25,6 +25,6 @@ public interface ISchemaVersionAware { interface ConfigurationInterface { /** Current version. */ - int SCHEMA_VERSION = 3; + int SCHEMA_VERSION = 4; } } diff --git a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/storage/serializer/SerializationManagerPostProcessor.java b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/storage/serializer/SerializationManagerPostProcessor.java index 53cf36ea8..37c24dc33 100644 --- a/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/storage/serializer/SerializationManagerPostProcessor.java +++ b/inspectit.shared.cs/src/main/java/rocks/inspectit/shared/cs/storage/serializer/SerializationManagerPostProcessor.java @@ -61,6 +61,14 @@ import rocks.inspectit.shared.cs.ci.sensor.method.impl.Log4jLoggingSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.PreparedStatementParameterSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.PreparedStatementSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteApacheHttpClientV40SensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJavaHttpServerSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJettyHttpClientV61ClientSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJmsClientSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJmsListenerServerSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteManualServerSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteSpringRestTemplateClientSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteUrlConnectionClientSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.StatementSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.TimerSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.platform.impl.ClassLoadingSensorConfig; @@ -400,6 +408,15 @@ private void registerClasses(SerializationManager serializationManager) { // NOC kryo.register(EMailListValidator.class, new FieldSerializer(kryo, EMailListValidator.class), nextRegistrationId++); kryo.register(AlertClosingReason.class, new EnumSerializer(AlertClosingReason.class), nextRegistrationId++); + // INSPECTIT-1921 + kryo.register(RemoteApacheHttpClientV40SensorConfig.class, new FieldSerializer<>(kryo, RemoteApacheHttpClientV40SensorConfig.class), nextRegistrationId++); + kryo.register(RemoteJettyHttpClientV61ClientSensorConfig.class, new FieldSerializer<>(kryo, RemoteJettyHttpClientV61ClientSensorConfig.class), nextRegistrationId++); + kryo.register(RemoteUrlConnectionClientSensorConfig.class, new FieldSerializer<>(kryo, RemoteUrlConnectionClientSensorConfig.class), nextRegistrationId++); + kryo.register(RemoteSpringRestTemplateClientSensorConfig.class, new FieldSerializer<>(kryo, RemoteSpringRestTemplateClientSensorConfig.class), nextRegistrationId++); + kryo.register(RemoteJavaHttpServerSensorConfig.class, new FieldSerializer<>(kryo, RemoteJavaHttpServerSensorConfig.class), nextRegistrationId++); + kryo.register(RemoteJmsClientSensorConfig.class, new FieldSerializer<>(kryo, RemoteJmsClientSensorConfig.class), nextRegistrationId++); + kryo.register(RemoteJmsListenerServerSensorConfig.class, new FieldSerializer<>(kryo, RemoteJmsListenerServerSensorConfig.class), nextRegistrationId++); + kryo.register(RemoteManualServerSensorConfig.class, new FieldSerializer<>(kryo, RemoteManualServerSensorConfig.class), nextRegistrationId++); } } diff --git a/inspectit.shared.cs/src/main/resources/META-INF/MANIFEST.MF b/inspectit.shared.cs/src/main/resources/META-INF/MANIFEST.MF index d3cc8a27e..850fa82c3 100644 --- a/inspectit.shared.cs/src/main/resources/META-INF/MANIFEST.MF +++ b/inspectit.shared.cs/src/main/resources/META-INF/MANIFEST.MF @@ -43,6 +43,8 @@ Export-Package: rocks.inspectit.shared.cs.ci, rocks.inspectit.shared.cs.ci.sensor.jmx, rocks.inspectit.shared.cs.ci.sensor.method, rocks.inspectit.shared.cs.ci.sensor.method.impl, + rocks.inspectit.shared.cs.ci.sensor.method.special, + rocks.inspectit.shared.cs.ci.sensor.method.special.impl, rocks.inspectit.shared.cs.ci.sensor.platform, rocks.inspectit.shared.cs.ci.sensor.platform.impl, rocks.inspectit.shared.cs.ci.strategy, diff --git a/inspectit.shared.cs/src/test/java/rocks/inspectit/shared/cs/indexing/aggregation/impl/HttpDataAggregatorTest.java b/inspectit.shared.cs/src/test/java/rocks/inspectit/shared/cs/indexing/aggregation/impl/HttpDataAggregatorTest.java index e64f021e0..fe93b6315 100644 --- a/inspectit.shared.cs/src/test/java/rocks/inspectit/shared/cs/indexing/aggregation/impl/HttpDataAggregatorTest.java +++ b/inspectit.shared.cs/src/test/java/rocks/inspectit/shared/cs/indexing/aggregation/impl/HttpDataAggregatorTest.java @@ -30,14 +30,14 @@ public class HttpDataAggregatorTest { public void aggregationWithInspectITHeaderTwoDifferent() { final HttpTimerData data = new HttpTimerData(); Map map1 = new HashMap<>(); - MapUtils.putAll(map1, new String[][] { { "inspectit", "tag1" } }); + MapUtils.putAll(map1, new String[][] { { HttpTimerData.INSPECTIT_TAGGING_HEADER, "tag1" } }); data.setHeaders(map1); data.getHttpInfo().setUri("URI"); data.getHttpInfo().setRequestMethod("GET"); final HttpTimerData data2 = new HttpTimerData(); Map map2 = new HashMap<>(); - MapUtils.putAll(map2, new String[][] { { "inspectit", "tag2" } }); + MapUtils.putAll(map2, new String[][] { { HttpTimerData.INSPECTIT_TAGGING_HEADER, "tag2" } }); data2.setHeaders(map2); data2.getHttpInfo().setUri("URI"); data2.getHttpInfo().setRequestMethod("GET"); @@ -56,7 +56,7 @@ public void aggregationWithInspectITHeaderTwoDifferent() { public void aggregationWithInspectITHeader() { final HttpTimerData data = new HttpTimerData(); Map map = new HashMap<>(); - MapUtils.putAll(map, new String[][] { { "inspectit", "tag1" } }); + MapUtils.putAll(map, new String[][] { { HttpTimerData.INSPECTIT_TAGGING_HEADER, "tag1" } }); data.setHeaders(map); data.getHttpInfo().setUri("URI"); @@ -72,7 +72,7 @@ public void aggregationWithInspectITHeader() { private static final long serialVersionUID = 6328525502662081826L; { - put("inspectit", "tag1"); + put(HttpTimerData.INSPECTIT_TAGGING_HEADER, "tag1"); } }); data2.getHttpInfo().setUri("URI"); diff --git a/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/InspectITImages.java b/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/InspectITImages.java index 328e46cde..7813b903d 100644 --- a/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/InspectITImages.java +++ b/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/InspectITImages.java @@ -201,6 +201,7 @@ public interface InspectITImages { String IMG_INFO_CIRCLE_FRAME = InspectITConstants.ICON_PATH_FUGUE + "information-frame.png"; String IMG_NAVIGATION_CIRCLE_FRAME = InspectITConstants.ICON_PATH_FUGUE + "navigation-frame.png"; String IMG_QUESTION_CIRCLE_FRAME = InspectITConstants.ICON_PATH_FUGUE + "question-frame.png"; + String IMG_REMOTE = InspectITConstants.ICON_PATH_FUGUE + "servers-network.png"; // Images originally from Fugue set we modified - license Creative Commons v3.0 String IMG_HTTP_PARAMETER = InspectITConstants.ICON_PATH_FUGUE + "http-parameter.png"; diff --git a/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/formatter/ImageFormatter.java b/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/formatter/ImageFormatter.java index d72e95320..953e3df67 100644 --- a/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/formatter/ImageFormatter.java +++ b/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/formatter/ImageFormatter.java @@ -36,6 +36,7 @@ import rocks.inspectit.shared.cs.ci.sensor.ISensorConfig; import rocks.inspectit.shared.cs.ci.sensor.exception.impl.ExceptionSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.jmx.JmxSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.AbstractRemoteSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.ConnectionSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.HttpSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.InvocationSequenceSensorConfig; @@ -438,6 +439,8 @@ public static Image getSensorConfigImage(Class sensorCl return InspectIT.getDefault().getImage(InspectITImages.IMG_THREADS_OVERVIEW); } else if (ObjectUtils.equals(sensorClass, JmxSensorConfig.class)) { return InspectIT.getDefault().getImage(InspectITImages.IMG_BEAN); + } else if (AbstractRemoteSensorConfig.class.isAssignableFrom(sensorClass)) { + return InspectIT.getDefault().getImage(InspectITImages.IMG_REMOTE); } return null; } diff --git a/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/formatter/TextFormatter.java b/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/formatter/TextFormatter.java index aef760c02..25b663c72 100644 --- a/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/formatter/TextFormatter.java +++ b/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/formatter/TextFormatter.java @@ -10,8 +10,8 @@ import java.util.Set; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; +import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.preference.JFacePreferences; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.StyledString; @@ -33,23 +33,6 @@ import rocks.inspectit.shared.cs.ci.assignment.ISensorAssignment; import rocks.inspectit.shared.cs.ci.assignment.impl.MethodSensorAssignment; import rocks.inspectit.shared.cs.ci.sensor.ISensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.exception.impl.ExceptionSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.jmx.JmxSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.method.impl.ConnectionSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.method.impl.HttpSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.method.impl.InvocationSequenceSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.method.impl.Log4jLoggingSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.method.impl.PreparedStatementParameterSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.method.impl.PreparedStatementSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.method.impl.StatementSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.method.impl.TimerSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.ClassLoadingSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.CompilationSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.CpuSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.MemorySensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.RuntimeSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.SystemSensorConfig; -import rocks.inspectit.shared.cs.ci.sensor.platform.impl.ThreadSensorConfig; import rocks.inspectit.shared.cs.communication.data.cmr.Alert; import rocks.inspectit.shared.cs.communication.data.cmr.WritingStatus; import rocks.inspectit.shared.cs.storage.LocalStorageData; @@ -694,42 +677,14 @@ public static String getSensorConfigName(ISensorConfig sensorConfig) { * @return Name or empty string if sensor name can be resolved. */ public static String getSensorConfigName(Class sensorClass) { - if (ObjectUtils.equals(sensorClass, ExceptionSensorConfig.class)) { - return "Exception Sensor"; - } else if (ObjectUtils.equals(sensorClass, ConnectionSensorConfig.class)) { - return "JDBC Connection Sensor"; - } else if (ObjectUtils.equals(sensorClass, HttpSensorConfig.class)) { - return "HTTP Sensor"; - } else if (ObjectUtils.equals(sensorClass, InvocationSequenceSensorConfig.class)) { - return "Invocation Sequence Sensor"; - } else if (ObjectUtils.equals(sensorClass, PreparedStatementParameterSensorConfig.class)) { - return "JDBC Prepared Statement Parameter Sensor"; - } else if (ObjectUtils.equals(sensorClass, PreparedStatementSensorConfig.class)) { - return "JDBC Prepared Statement Sensor"; - } else if (ObjectUtils.equals(sensorClass, StatementSensorConfig.class)) { - return "JDBC Statement Sensor"; - } else if (ObjectUtils.equals(sensorClass, TimerSensorConfig.class)) { - return "Timer Sensor"; - } else if (ObjectUtils.equals(sensorClass, Log4jLoggingSensorConfig.class)) { - return "Logging Sensor for log4j "; - } else if (ObjectUtils.equals(sensorClass, ClassLoadingSensorConfig.class)) { - return "Class Loading Information"; - } else if (ObjectUtils.equals(sensorClass, CompilationSensorConfig.class)) { - return "Compilation Information"; - } else if (ObjectUtils.equals(sensorClass, CpuSensorConfig.class)) { - return "CPU Information"; - } else if (ObjectUtils.equals(sensorClass, MemorySensorConfig.class)) { - return "Memory Information"; - } else if (ObjectUtils.equals(sensorClass, RuntimeSensorConfig.class)) { - return "Runtime Information"; - } else if (ObjectUtils.equals(sensorClass, SystemSensorConfig.class)) { - return "System Information"; - } else if (ObjectUtils.equals(sensorClass, ThreadSensorConfig.class)) { - return "Thread Information"; - } else if (ObjectUtils.equals(sensorClass, JmxSensorConfig.class)) { - return "JMX Sensor"; + // we expect that all configurations have SENSOR_NAME field + try { + ISensorConfig sensorInstance = sensorClass.newInstance(); + return sensorInstance.getName(); + } catch (Exception e) { + InspectIT.getDefault().log(IStatus.WARNING, "Can not load sensor name for class " + sensorClass.getSimpleName()); + return null; } - return null; } /** diff --git a/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/model/SensorTypeEnum.java b/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/model/SensorTypeEnum.java index 469d077cb..38b436705 100644 --- a/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/model/SensorTypeEnum.java +++ b/inspectit.ui.rcp/src/main/java/rocks/inspectit/ui/rcp/model/SensorTypeEnum.java @@ -16,6 +16,14 @@ import rocks.inspectit.shared.cs.ci.sensor.method.impl.Log4jLoggingSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.PreparedStatementParameterSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.PreparedStatementSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteApacheHttpClientV40SensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJavaHttpServerSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJettyHttpClientV61ClientSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJmsClientSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteJmsListenerServerSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteManualServerSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteSpringRestTemplateClientSensorConfig; +import rocks.inspectit.shared.cs.ci.sensor.method.impl.RemoteUrlConnectionClientSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.StatementSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.impl.TimerSensorConfig; import rocks.inspectit.shared.cs.ci.sensor.method.special.impl.ClassLoadingDelegationSensorConfig; @@ -96,7 +104,23 @@ public enum SensorTypeEnum { /** MBean server interceptor. */ MBEAN_SERVER_INTERCEPTOR(MBeanServerInterceptorSensorConfig.CLASS_NAME, InspectITImages.IMG_BEAN, false), /** The alert invocation sequence sensor type. */ - ALERT_INVOCATION(InvocationSequenceSensorConfig.CLASS_NAME + "#alert", InspectITImages.IMG_ALARM_INVOCATION); + ALERT_INVOCATION(InvocationSequenceSensorConfig.CLASS_NAME + "#alert", InspectITImages.IMG_ALARM_INVOCATION), + /** The Remote Apache sensor. */ + REMOTE_APACHE_HTTP_CLIENT_V40(RemoteApacheHttpClientV40SensorConfig.CLASS_NAME, InspectITImages.IMG_REMOTE, false), + /** The Remote UrlConnection sensor. */ + REMOTE_HTTP_URL_CONNECTION(RemoteUrlConnectionClientSensorConfig.CLASS_NAME, InspectITImages.IMG_REMOTE, false), + /** The Remote UrlConnection sensor. */ + REMOTE_SPRING_REST_TEMPLATE_CLIENT(RemoteSpringRestTemplateClientSensorConfig.CLASS_NAME, InspectITImages.IMG_REMOTE, false), + /** The Remote Jetty sensor. */ + REMOTE_JETTY_HTTP_CLIENT_V61(RemoteJettyHttpClientV61ClientSensorConfig.CLASS_NAME, InspectITImages.IMG_REMOTE, false), + /** The Remote http client sensor. */ + REMOTE_JAVA_HTTP_SERVER(RemoteJavaHttpServerSensorConfig.CLASS_NAME, InspectITImages.IMG_REMOTE, false), + /** The Remote jms client sensor. */ + REMOTE_JMS_CLIENT(RemoteJmsClientSensorConfig.CLASS_NAME, InspectITImages.IMG_REMOTE, false), + /** The Remote jms listener sensor. */ + REMOTE_JMS_LISTENER(RemoteJmsListenerServerSensorConfig.CLASS_NAME, InspectITImages.IMG_REMOTE, false), + /** The manual Remote server sensor. */ + REMOTE_MANUAL_SERVER(RemoteManualServerSensorConfig.CLASS_NAME, InspectITImages.IMG_REMOTE, false); /** * The LOOKUP map which is used to get an element of the enumeration when passing the full diff --git a/inspectit.ui.rcp/src/main/resources/icons/fugue/servers-network.png b/inspectit.ui.rcp/src/main/resources/icons/fugue/servers-network.png new file mode 100644 index 000000000..68a39f299 Binary files /dev/null and b/inspectit.ui.rcp/src/main/resources/icons/fugue/servers-network.png differ diff --git a/resources/oomph/inspectIT.setup b/resources/oomph/inspectIT.setup index 5c400b1f3..200fe1d53 100644 --- a/resources/oomph/inspectIT.setup +++ b/resources/oomph/inspectIT.setup @@ -1560,7 +1560,7 @@ + value="true"/>