Code originally from https://github.com/census-instrumentation/opencensus-java/blob/ + * 446e9bde9b1f6c0317e3f310644997e5d6d5eab2/api/src/main/java/io/opencensus/trace/SpanContext.java
+ * @since 0.5 + */ +@Immutable +final class SpanContext { + + private final TraceId traceId; + + private final SpanId spanId; + + private final TraceOptions traceOptions; + + private final Tracestate tracestate; + + /** + * Creates a new {@code SpanContext} with the given identifiers and options. + * + * @param traceId the trace identifier of the span context. + * @param spanId the span identifier of the span context. + * @param traceOptions the trace options for the span context. + * @param tracestate the trace state for the span context. + * @return a new {@code SpanContext} with the given identifiers and options. + * @since 0.16 + */ + static SpanContext create( + TraceId traceId, SpanId spanId, TraceOptions traceOptions, Tracestate tracestate) { + return new SpanContext(traceId, spanId, traceOptions, tracestate); + } + + /** + * Returns the trace identifier associated with this {@code SpanContext}. + * + * @return the trace identifier associated with this {@code SpanContext}. + * @since 0.5 + */ + TraceId getTraceId() { + return traceId; + } + + /** + * Returns the span identifier associated with this {@code SpanContext}. + * + * @return the span identifier associated with this {@code SpanContext}. + * @since 0.5 + */ + SpanId getSpanId() { + return spanId; + } + + /** + * Returns the {@code TraceOptions} associated with this {@code SpanContext}. + * + * @return the {@code TraceOptions} associated with this {@code SpanContext}. + * @since 0.5 + */ + TraceOptions getTraceOptions() { + return traceOptions; + } + + /** + * Returns the {@code Tracestate} associated with this {@code SpanContext}. + * + * @return the {@code Tracestate} associated with this {@code SpanContext}. + * @since 0.5 + */ + Tracestate getTracestate() { + return tracestate; + } + + /** + * Returns true if this {@code SpanContext} is valid. + * + * @return true if this {@code SpanContext} is valid. + * @since 0.5 + */ + boolean isValid() { + return traceId.isValid() && spanId.isValid(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (obj == this) { + return true; + } + + if (!(obj instanceof SpanContext)) { + return false; + } + + SpanContext that = (SpanContext) obj; + return traceId.equals(that.traceId) + && spanId.equals(that.spanId) + && traceOptions.equals(that.traceOptions); + } + + @Override + public int hashCode() { + return Arrays.hashCode(new Object[]{traceId, spanId, traceOptions}); + } + + private SpanContext( + TraceId traceId, SpanId spanId, TraceOptions traceOptions, Tracestate tracestate) { + this.traceId = traceId; + this.spanId = spanId; + this.traceOptions = traceOptions; + this.tracestate = tracestate; + } +} \ No newline at end of file diff --git a/sdk/src/main/java/io/dapr/internal/opencensus/SpanId.java b/sdk/src/main/java/io/dapr/internal/opencensus/SpanId.java new file mode 100644 index 000000000..b7481d59c --- /dev/null +++ b/sdk/src/main/java/io/dapr/internal/opencensus/SpanId.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.internal.opencensus; + +import javax.annotation.concurrent.Immutable; + +/** + * A class that represents a span identifier. A valid span identifier is an 8-byte array with at + * least one non-zero byte. + * + *Code originally from https://github.com/census-instrumentation/opencensus-java/blob/ + * 446e9bde9b1f6c0317e3f310644997e5d6d5eab2/api/src/main/java/io/opencensus/trace/TraceId.java
+ * @since 0.5 + */ +@Immutable +final class SpanId implements ComparableCode originally from https://github.com/census-instrumentation/opencensus-java/blob/ + * 446e9bde9b1f6c0317e3f310644997e5d6d5eab2/impl_core/src/main/java/io/opencensus/implcore/ + * trace/propagation/TraceContextFormat.java
+ */ +class TraceContextFormat { + + private static final Tracestate TRACESTATE_DEFAULT = Tracestate.builder().build(); + private static final String TRACEPARENT = "traceparent"; + private static final String TRACESTATE = "tracestate"; + + private static final Metadata.KeyCode originally from https://github.com/census-instrumentation/opencensus-java/blob/ + * 446e9bde9b1f6c0317e3f310644997e5d6d5eab2/api/src/main/java/io/opencensus/trace/TraceId.java.
+ * @since 0.5 + */ +@Immutable +final class TraceId implements ComparableCode originally from https://github.com/census-instrumentation/opencensus-java/blob/ + * 446e9bde9b1f6c0317e3f310644997e5d6d5eab2/api/src/main/java/io/opencensus/trace/TraceOptions.java
+ * @since 0.5 + */ +@Immutable +final class TraceOptions { + /** + * The size in bytes of the {@code TraceOptions}. + * + * @since 0.5 + */ + static final int SIZE = 1; + + // The set of enabled features is determined by all the enabled bits. + private final byte options; + + // Creates a new {@code TraceOptions} with the given options. + private TraceOptions(byte options) { + this.options = options; + } + + /** + * Returns a {@code TraceOption} built from a lowercase base16 representation. + * + * @param src the lowercase base16 representation. + * @param srcOffset the offset in the buffer where the representation of the {@code TraceOptions} + * begins. + * @return a {@code TraceOption} built from a lowercase base16 representation. + * @throws NullPointerException if {@code src} is null. + * @throws IllegalArgumentException if {@code src.length} is not {@code 2 * TraceOption.SIZE} OR + * if the {@code str} has invalid characters. + * @since 0.18 + */ + static TraceOptions fromLowerBase16(CharSequence src, int srcOffset) { + return new TraceOptions(BigendianEncoding.byteFromBase16String(src, srcOffset)); + } + + /** + * Copies the byte representations of the {@code TraceOptions} into the {@code dest} beginning at + * the {@code destOffset} offset. + * + *Equivalent with (but faster because it avoids any new allocations): + * + *
{@code
+ * System.arraycopy(getBytes(), 0, dest, destOffset, TraceOptions.SIZE);
+ * }
+ *
+ * @param dest the destination buffer.
+ * @param destOffset the starting offset in the destination buffer.
+ * @throws NullPointerException if {@code dest} is null.
+ * @throws IndexOutOfBoundsException if {@code destOffset+TraceOptions.SIZE} is greater than
+ * {@code dest.length}.
+ * @since 0.5
+ */
+ void copyBytesTo(byte[] dest, int destOffset) {
+ Utils.checkIndex(destOffset, dest.length);
+ dest[destOffset] = options;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+
+ if (obj == this) {
+ return true;
+ }
+
+ if (!(obj instanceof TraceOptions)) {
+ return false;
+ }
+
+ TraceOptions that = (TraceOptions) obj;
+ return options == that.options;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(new byte[]{options});
+ }
+
+}
\ No newline at end of file
diff --git a/sdk/src/main/java/io/dapr/internal/opencensus/Tracestate.java b/sdk/src/main/java/io/dapr/internal/opencensus/Tracestate.java
new file mode 100644
index 000000000..c87031b37
--- /dev/null
+++ b/sdk/src/main/java/io/dapr/internal/opencensus/Tracestate.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) Microsoft Corporation.
+ * Licensed under the MIT License.
+ */
+
+package io.dapr.internal.opencensus;
+
+import javax.annotation.concurrent.Immutable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Carries tracing-system specific context in a list of key-value pairs. TraceState allows different
+ * vendors propagate additional information and inter-operate with their legacy Id formats.
+ *
+ * Implementation is optimized for a small list of key-value pairs. + * + *
Key is opaque string up to 256 characters printable. It MUST begin with a lowercase letter, + * and can only contain lowercase letters a-z, digits 0-9, underscores _, dashes -, asterisks *, and + * forward slashes /. + * + *
Value is opaque string up to 256 characters printable ASCII RFC0020 characters (i.e., the + * range 0x20 to 0x7E) except comma , and =. + * + *
Code originally from https://github.com/census-instrumentation/opencensus-java/blob/ + * 446e9bde9b1f6c0317e3f310644997e5d6d5eab2/api/src/main/java/io/opencensus/trace/Tracestate.java
+ * @since 0.16 + */ +@Immutable +class Tracestate { + private static final int KEY_MAX_SIZE = 256; + private static final int VALUE_MAX_SIZE = 256; + private static final int MAX_KEY_VALUE_PAIRS = 32; + + private final ListCode originally from https://github.com/census-instrumentation/opencensus-java/blob/ + * 446e9bde9b1f6c0317e3f310644997e5d6d5eab2/api/src/main/java/io/opencensus/internal/Utils.java
+ */ +final class Utils { + + private Utils() { + } + + /** + * Throws an {@link IllegalArgumentException} if the argument is false. This method is similar to + * {@code Preconditions.checkArgument(boolean, Object)} from Guava. + * + * @param isValid whether the argument check passed. + * @param errorMessage the message to use for the exception. Will be converted to a string using + * {@link String#valueOf(Object)}. + */ + static void checkArgument( + boolean isValid, @javax.annotation.Nullable Object errorMessage) { + if (!isValid) { + throw new IllegalArgumentException(String.valueOf(errorMessage)); + } + } + + /** + * Throws an {@link IllegalArgumentException} if the argument is false. This method is similar to + * {@code Preconditions.checkArgument(boolean, Object)} from Guava. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the check fail. The + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted + * message in + * square braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to strings using {@link String#valueOf(Object)}. + * @throws IllegalArgumentException if {@code expression} is false + * @throws NullPointerException if the check fails and either {@code errorMessageTemplate} or + * {@code errorMessageArgs} is null (don't let this happen) + */ + static void checkArgument( + boolean expression, + String errorMessageTemplate, + @javax.annotation.Nullable Object... errorMessageArgs) { + if (!expression) { + throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Throws an {@link IllegalStateException} if the argument is false. This method is similar to + * {@code Preconditions.checkState(boolean, Object)} from Guava. + * + * @param isValid whether the state check passed. + * @param errorMessage the message to use for the exception. Will be converted to a string using + * {@link String#valueOf(Object)}. + */ + static void checkState(boolean isValid, @javax.annotation.Nullable Object errorMessage) { + if (!isValid) { + throw new IllegalStateException(String.valueOf(errorMessage)); + } + } + + /** + * Validates an index in an array or other container. This method throws an {@link + * IllegalArgumentException} if the size is negative and throws an {@link + * IndexOutOfBoundsException} if the index is negative or greater than or equal to the size. This + * method is similar to {@code Preconditions.checkElementIndex(int, int)} from Guava. + * + * @param index the index to validate. + * @param size the size of the array or container. + */ + static void checkIndex(int index, int size) { + if (size < 0) { + throw new IllegalArgumentException("Negative size: " + size); + } + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException("Index out of bounds: size=" + size + ", index=" + index); + } + } + + /** + * Throws a {@link NullPointerException} if the argument is null. This method is similar to {@code + * Preconditions.checkNotNull(Object, Object)} from Guava. + * + * @param arg the argument to check for null. + * @param errorMessage the message to use for the exception. Will be converted to a string using + * {@link String#valueOf(Object)}. + * @paramCopied from {@code Preconditions.format(String, Object...)} from Guava
+ *
+ * @param template a non-null string containing 0 or more {@code %s} placeholders.
+ * @param args the arguments to be substituted into the message template. Arguments are converted
+ * to strings using {@link String#valueOf(Object)}. Arguments can be null.
+ */
+ // Note that this is somewhat-improperly used from Verify.java as well.
+ private static String format(String template, @javax.annotation.Nullable Object... args) {
+ // If no arguments return the template.
+ if (args == null) {
+ return template;
+ }
+
+ // start substituting the arguments into the '%s' placeholders
+ StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
+ int templateStart = 0;
+ int i = 0;
+ while (i < args.length) {
+ int placeholderStart = template.indexOf("%s", templateStart);
+ if (placeholderStart == -1) {
+ break;
+ }
+ builder.append(template, templateStart, placeholderStart);
+ builder.append(args[i++]);
+ templateStart = placeholderStart + 2;
+ }
+ builder.append(template, templateStart, template.length());
+
+ // if we run out of placeholders, append the extra args in square braces
+ if (i < args.length) {
+ builder.append(" [");
+ builder.append(args[i++]);
+ while (i < args.length) {
+ builder.append(", ");
+ builder.append(args[i++]);
+ }
+ builder.append(']');
+ }
+
+ return builder.toString();
+ }
+}
\ No newline at end of file
diff --git a/sdk/src/test/java/io/dapr/client/DaprClientGrpcTelemetryTest.java b/sdk/src/test/java/io/dapr/client/DaprClientGrpcTelemetryTest.java
new file mode 100644
index 000000000..13876f9b2
--- /dev/null
+++ b/sdk/src/test/java/io/dapr/client/DaprClientGrpcTelemetryTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) Microsoft Corporation.
+ * Licensed under the MIT License.
+ */
+
+package io.dapr.client;
+
+import io.dapr.client.domain.HttpExtension;
+import io.dapr.client.domain.InvokeMethodRequest;
+import io.dapr.client.domain.InvokeMethodRequestBuilder;
+import io.dapr.client.domain.Response;
+import io.dapr.serializer.DefaultObjectSerializer;
+import io.dapr.utils.TypeRef;
+import io.dapr.v1.CommonProtos;
+import io.dapr.v1.DaprGrpc;
+import io.grpc.ManagedChannel;
+import io.grpc.Metadata;
+import io.grpc.ServerCall;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerInterceptor;
+import io.grpc.ServerInterceptors;
+import io.grpc.ServerServiceDefinition;
+import io.grpc.inprocess.InProcessChannelBuilder;
+import io.grpc.inprocess.InProcessServerBuilder;
+import io.grpc.testing.GrpcCleanupRule;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import reactor.core.publisher.Mono;
+import reactor.util.context.Context;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(Parameterized.class)
+public class DaprClientGrpcTelemetryTest {
+
+ private static final Metadata.Key