aboutsummaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
authorYang Song <songy23@users.noreply.github.com>2019-04-10 12:26:22 -0700
committerGitHub <noreply@github.com>2019-04-10 12:26:22 -0700
commit44906aae202aee946ec7f994f32077dacf13ef05 (patch)
tree14e2d65e377285a845dd7302a2835b44bcc9274e /api
parent8564d8146a315370ccd1c7ccf3d98adc9c128098 (diff)
downloadplatform_external_opencensus-java-44906aae202aee946ec7f994f32077dacf13ef05.tar.gz
platform_external_opencensus-java-44906aae202aee946ec7f994f32077dacf13ef05.tar.bz2
platform_external_opencensus-java-44906aae202aee946ec7f994f32077dacf13ef05.zip
Tags: Add HTTP text serializer API. (#1800)
* Tags: Add HTTP text serializer API. * Add setter/getter and inject/extract to support encoding tags to multiple strings. * Import checker framework annotation. * Update changelog
Diffstat (limited to 'api')
-rw-r--r--api/src/main/java/io/opencensus/tags/NoopTags.java55
-rw-r--r--api/src/main/java/io/opencensus/tags/propagation/TagContextTextFormat.java165
-rw-r--r--api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java12
-rw-r--r--api/src/test/java/io/opencensus/tags/NoopTagsTest.java69
4 files changed, 300 insertions, 1 deletions
diff --git a/api/src/main/java/io/opencensus/tags/NoopTags.java b/api/src/main/java/io/opencensus/tags/NoopTags.java
index 4d1d3b8d..c4baadb5 100644
--- a/api/src/main/java/io/opencensus/tags/NoopTags.java
+++ b/api/src/main/java/io/opencensus/tags/NoopTags.java
@@ -20,12 +20,20 @@ import io.opencensus.common.Scope;
import io.opencensus.internal.NoopScope;
import io.opencensus.internal.Utils;
import io.opencensus.tags.propagation.TagContextBinarySerializer;
+import io.opencensus.tags.propagation.TagContextDeserializationException;
+import io.opencensus.tags.propagation.TagContextSerializationException;
+import io.opencensus.tags.propagation.TagContextTextFormat;
import io.opencensus.tags.propagation.TagPropagationComponent;
import java.util.Collections;
import java.util.Iterator;
+import java.util.List;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;
+/*>>>
+import org.checkerframework.checker.nullness.qual.NonNull;
+*/
+
/** No-op implementations of tagging classes. */
final class NoopTags {
@@ -80,8 +88,17 @@ final class NoopTags {
return NoopTagContextBinarySerializer.INSTANCE;
}
+ /**
+ * Returns a {@code TagContextTextFormat} that serializes all {@code TagContext}s to empty strings
+ * and deserializes all inputs to empty {@code TagContext}s.
+ */
+ static TagContextTextFormat getNoopTagContextTextSerializer() {
+ return NoopTagContextTextFormat.INSTANCE;
+ }
+
@ThreadSafe
private static final class NoopTagsComponent extends TagsComponent {
+
private volatile boolean isRead;
@Override
@@ -110,6 +127,7 @@ final class NoopTags {
@Immutable
private static final class NoopTagger extends Tagger {
+
static final Tagger INSTANCE = new NoopTagger();
@Override
@@ -147,6 +165,7 @@ final class NoopTags {
@Immutable
private static final class NoopTagContextBuilder extends TagContextBuilder {
+
static final TagContextBuilder INSTANCE = new NoopTagContextBuilder();
@Override
@@ -184,6 +203,7 @@ final class NoopTags {
@Immutable
private static final class NoopTagContext extends TagContext {
+
static final TagContext INSTANCE = new NoopTagContext();
// TODO(sebright): Is there any way to let the user know that their tags were ignored?
@@ -195,16 +215,23 @@ final class NoopTags {
@Immutable
private static final class NoopTagPropagationComponent extends TagPropagationComponent {
+
static final TagPropagationComponent INSTANCE = new NoopTagPropagationComponent();
@Override
public TagContextBinarySerializer getBinarySerializer() {
return getNoopTagContextBinarySerializer();
}
+
+ @Override
+ public TagContextTextFormat getCorrelationContextFormat() {
+ return getNoopTagContextTextSerializer();
+ }
}
@Immutable
private static final class NoopTagContextBinarySerializer extends TagContextBinarySerializer {
+
static final TagContextBinarySerializer INSTANCE = new NoopTagContextBinarySerializer();
static final byte[] EMPTY_BYTE_ARRAY = {};
@@ -220,4 +247,32 @@ final class NoopTags {
return getNoopTagContext();
}
}
+
+ @Immutable
+ private static final class NoopTagContextTextFormat extends TagContextTextFormat {
+
+ static final NoopTagContextTextFormat INSTANCE = new NoopTagContextTextFormat();
+
+ @Override
+ public List<String> fields() {
+ return Collections.<String>emptyList();
+ }
+
+ @Override
+ public <C /*>>> extends @NonNull Object*/> void inject(
+ TagContext tagContext, C carrier, Setter<C> setter)
+ throws TagContextSerializationException {
+ Utils.checkNotNull(tagContext, "tagContext");
+ Utils.checkNotNull(carrier, "carrier");
+ Utils.checkNotNull(setter, "setter");
+ }
+
+ @Override
+ public <C /*>>> extends @NonNull Object*/> TagContext extract(C carrier, Getter<C> getter)
+ throws TagContextDeserializationException {
+ Utils.checkNotNull(carrier, "carrier");
+ Utils.checkNotNull(getter, "getter");
+ return getNoopTagContext();
+ }
+ }
}
diff --git a/api/src/main/java/io/opencensus/tags/propagation/TagContextTextFormat.java b/api/src/main/java/io/opencensus/tags/propagation/TagContextTextFormat.java
new file mode 100644
index 00000000..af22827e
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/propagation/TagContextTextFormat.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2019, OpenCensus Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.opencensus.tags.propagation;
+
+import io.opencensus.tags.TagContext;
+import java.util.List;
+import javax.annotation.Nullable;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.NonNull;
+*/
+
+/**
+ * Object for injecting and extracting {@link TagContext} as text into carriers that travel in-band
+ * across process boundaries. Tags are often encoded as messaging or RPC request headers.
+ *
+ * <p>When using http, the carrier of propagated data on both the client (injector) and server
+ * (extractor) side is usually an http request. Propagation is usually implemented via library-
+ * specific request interceptors, where the client-side injects tags and the server-side extracts
+ * them.
+ *
+ * <p>Example of usage on the client:
+ *
+ * <pre>{@code
+ * private static final Tagger tagger = Tags.getTagger();
+ * private static final TagContextTextFormat textFormat =
+ * Tags.getPropagationComponent().getCorrelationContextFormat();
+ * private static final TagContextTextFormat.Setter setter =
+ * new TagContextTextFormat.Setter<HttpURLConnection>() {
+ * public void put(HttpURLConnection carrier, String key, String value) {
+ * carrier.setRequestProperty(field, value);
+ * }
+ * };
+ *
+ * void makeHttpRequest() {
+ * TagContext tagContext = tagger.emptyBuilder().put(K, V).build();
+ * try (Scope s = tagger.withTagContext(tagContext)) {
+ * HttpURLConnection connection =
+ * (HttpURLConnection) new URL("http://myserver").openConnection();
+ * textFormat.inject(tagContext, connection, httpURLConnectionSetter);
+ * // Send the request, wait for response and maybe set the status if not ok.
+ * }
+ * }
+ * }</pre>
+ *
+ * <p>Example of usage on the server:
+ *
+ * <pre>{@code
+ * private static final Tagger tagger = Tags.getTagger();
+ * private static final TagContextTextFormat textFormat =
+ * Tags.getPropagationComponent().getCorrelationContextFormat();
+ * private static final TagContextTextFormat.Getter<HttpRequest> getter = ...;
+ *
+ * void onRequestReceived(HttpRequest request) {
+ * TagContext tagContext = textFormat.extract(request, getter);
+ * try (Scope s = tagger.withTagContext(tagContext)) {
+ * // Handle request and send response back.
+ * }
+ * }
+ * }</pre>
+ *
+ * @since 0.21
+ */
+public abstract class TagContextTextFormat {
+
+ /**
+ * The propagation fields defined. If your carrier is reused, you should delete the fields here
+ * before calling {@link #inject(TagContext, Object, Setter)}.
+ *
+ * <p>For example, if the carrier is a single-use or immutable request object, you don't need to
+ * clear fields as they couldn't have been set before. If it is a mutable, retryable object,
+ * successive calls should clear these fields first.
+ *
+ * @since 0.21
+ */
+ // The use cases of this are:
+ // * allow pre-allocation of fields, especially in systems like gRPC Metadata
+ // * allow a single-pass over an iterator (ex OpenTracing has no getter in TextMap)
+ public abstract List<String> fields();
+
+ /**
+ * Injects the tag context downstream. For example, as http headers.
+ *
+ * @param tagContext the tag context.
+ * @param carrier holds propagation fields. For example, an outgoing message or http request.
+ * @param setter invoked for each propagation key to add or remove.
+ * @throws TagContextSerializationException if the given tag context cannot be serialized.
+ * @since 0.21
+ */
+ public abstract <C /*>>> extends @NonNull Object*/> void inject(
+ TagContext tagContext, C carrier, Setter<C> setter) throws TagContextSerializationException;
+
+ /**
+ * Class that allows a {@code TagContextTextFormat} to set propagated fields into a carrier.
+ *
+ * <p>{@code Setter} is stateless and allows to be saved as a constant to avoid runtime
+ * allocations.
+ *
+ * @param <C> carrier of propagation fields, such as an http request
+ * @since 0.21
+ */
+ public abstract static class Setter<C> {
+
+ /**
+ * Replaces a propagated field with the given value.
+ *
+ * <p>For example, a setter for an {@link java.net.HttpURLConnection} would be the method
+ * reference {@link java.net.HttpURLConnection#addRequestProperty(String, String)}
+ *
+ * @param carrier holds propagation fields. For example, an outgoing message or http request.
+ * @param key the key of the field.
+ * @param value the value of the field.
+ * @since 0.21
+ */
+ public abstract void put(C carrier, String key, String value);
+ }
+
+ /**
+ * Extracts the tag context from upstream. For example, as http headers.
+ *
+ * @param carrier holds propagation fields. For example, an outgoing message or http request.
+ * @param getter invoked for each propagation key to get.
+ * @throws TagContextDeserializationException if the input is invalid
+ * @since 0.21
+ */
+ public abstract <C /*>>> extends @NonNull Object*/> TagContext extract(
+ C carrier, Getter<C> getter) throws TagContextDeserializationException;
+
+ /**
+ * Class that allows a {@code TagContextTextFormat} to read propagated fields from a carrier.
+ *
+ * <p>{@code Getter} is stateless and allows to be saved as a constant to avoid runtime
+ * allocations.
+ *
+ * @param <C> carrier of propagation fields, such as an http request
+ * @since 0.21
+ */
+ public abstract static class Getter<C> {
+
+ /**
+ * Returns the first value of the given propagation {@code key} or returns {@code null}.
+ *
+ * @param carrier carrier of propagation fields, such as an http request
+ * @param key the key of the field.
+ * @return the first value of the given propagation {@code key} or returns {@code null}.
+ * @since 0.21
+ */
+ @Nullable
+ public abstract String get(C carrier, String key);
+ }
+}
diff --git a/api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java b/api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java
index 6ececa79..1c4d0ca5 100644
--- a/api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java
+++ b/api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java
@@ -23,7 +23,6 @@ import io.opencensus.tags.TagContext;
*
* @since 0.8
*/
-// TODO(sebright): Add an HTTP serializer.
public abstract class TagPropagationComponent {
/**
@@ -33,4 +32,15 @@ public abstract class TagPropagationComponent {
* @since 0.8
*/
public abstract TagContextBinarySerializer getBinarySerializer();
+
+ /**
+ * Returns the {@link TagContextTextFormat} for this implementation.
+ *
+ * <p>OpenCensus uses W3C Correlation Context as the HTTP text format. For more details, see <a
+ * href="https://github.com/w3c/correlation-context">correlation-context</a>.
+ *
+ * @return the {@code TagContextTextFormat} for this implementation.
+ * @since 0.21
+ */
+ public abstract TagContextTextFormat getCorrelationContextFormat();
}
diff --git a/api/src/test/java/io/opencensus/tags/NoopTagsTest.java b/api/src/test/java/io/opencensus/tags/NoopTagsTest.java
index e7c1ebfd..574af15a 100644
--- a/api/src/test/java/io/opencensus/tags/NoopTagsTest.java
+++ b/api/src/test/java/io/opencensus/tags/NoopTagsTest.java
@@ -23,8 +23,12 @@ import io.opencensus.internal.NoopScope;
import io.opencensus.tags.propagation.TagContextBinarySerializer;
import io.opencensus.tags.propagation.TagContextDeserializationException;
import io.opencensus.tags.propagation.TagContextSerializationException;
+import io.opencensus.tags.propagation.TagContextTextFormat;
+import io.opencensus.tags.propagation.TagContextTextFormat.Getter;
+import io.opencensus.tags.propagation.TagContextTextFormat.Setter;
import java.util.Arrays;
import java.util.Iterator;
+import javax.annotation.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -46,6 +50,21 @@ public final class NoopTagsTest {
}
};
+ private static final Setter<Object> NOOP_SETTER =
+ new Setter<Object>() {
+ @Override
+ public void put(Object carrier, String key, String value) {}
+ };
+
+ private static final Getter<Object> NOOP_GETTER =
+ new Getter<Object>() {
+ @Nullable
+ @Override
+ public String get(Object carrier, String key) {
+ return null;
+ }
+ };
+
@Rule public final ExpectedException thrown = ExpectedException.none();
@Test
@@ -157,6 +176,8 @@ public final class NoopTagsTest {
public void noopTagPropagationComponent() {
assertThat(NoopTags.getNoopTagPropagationComponent().getBinarySerializer())
.isSameAs(NoopTags.getNoopTagContextBinarySerializer());
+ assertThat(NoopTags.getNoopTagPropagationComponent().getCorrelationContextFormat())
+ .isSameAs(NoopTags.getNoopTagContextTextSerializer());
}
@Test
@@ -183,4 +204,52 @@ public final class NoopTagsTest {
thrown.expect(NullPointerException.class);
noopSerializer.fromByteArray(null);
}
+
+ @Test
+ public void noopTagContextTextFormat()
+ throws TagContextDeserializationException, TagContextSerializationException {
+ NoopTags.getNoopTagContextTextSerializer().inject(TAG_CONTEXT, new Object(), NOOP_SETTER);
+ assertThat(NoopTags.getNoopTagContextTextSerializer().extract(new Object(), NOOP_GETTER))
+ .isEqualTo(NoopTags.getNoopTagContext());
+ }
+
+ @Test
+ public void noopTagContextTextFormat_inject_DisallowsNullTagContext()
+ throws TagContextSerializationException {
+ TagContextTextFormat noopSerializer = NoopTags.getNoopTagContextTextSerializer();
+ thrown.expect(NullPointerException.class);
+ noopSerializer.inject(null, new Object(), NOOP_SETTER);
+ }
+
+ @Test
+ public void noopTagContextTextFormat_inject_DisallowsNullCarrier()
+ throws TagContextSerializationException {
+ TagContextTextFormat noopSerializer = NoopTags.getNoopTagContextTextSerializer();
+ thrown.expect(NullPointerException.class);
+ noopSerializer.inject(TAG_CONTEXT, null, NOOP_SETTER);
+ }
+
+ @Test
+ public void noopTagContextTextFormat_inject_DisallowsNullSetter()
+ throws TagContextSerializationException {
+ TagContextTextFormat noopSerializer = NoopTags.getNoopTagContextTextSerializer();
+ thrown.expect(NullPointerException.class);
+ noopSerializer.inject(TAG_CONTEXT, new Object(), null);
+ }
+
+ @Test
+ public void noopTagContextTextFormat_extract_DisallowsNullCarrier()
+ throws TagContextDeserializationException {
+ TagContextTextFormat noopSerializer = NoopTags.getNoopTagContextTextSerializer();
+ thrown.expect(NullPointerException.class);
+ noopSerializer.extract(null, NOOP_GETTER);
+ }
+
+ @Test
+ public void noopTagContextTextFormat_extract_DisallowsNullGetter()
+ throws TagContextDeserializationException {
+ TagContextTextFormat noopSerializer = NoopTags.getNoopTagContextTextSerializer();
+ thrown.expect(NullPointerException.class);
+ noopSerializer.extract(new Object(), null);
+ }
}