From d0d836136149d7379b50bd6fb8d81e4749df3fd8 Mon Sep 17 00:00:00 2001 From: Bogdan Drutu Date: Thu, 2 Aug 2018 15:57:31 +0300 Subject: Add the initial version of the TraceState class. (#1300) * Add the initial version of the TraceState class. * Add builder pattern. * Clean API and add tests. * Rename addOrUpdate to set and apply trailing rules for value. * Apply google java format. * Fix check framework. --- .../main/java/io/opencensus/trace/Tracestate.java | 265 +++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 api/src/main/java/io/opencensus/trace/Tracestate.java (limited to 'api/src/main/java/io/opencensus/trace/Tracestate.java') diff --git a/api/src/main/java/io/opencensus/trace/Tracestate.java b/api/src/main/java/io/opencensus/trace/Tracestate.java new file mode 100644 index 00000000..63d32fd3 --- /dev/null +++ b/api/src/main/java/io/opencensus/trace/Tracestate.java @@ -0,0 +1,265 @@ +/* + * Copyright 2018, 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.trace; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.ExperimentalApi; +import io.opencensus.internal.Utils; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.concurrent.Immutable; + +/** + * 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 =. + * + * @since 0.16 + */ +@Immutable +@AutoValue +@ExperimentalApi +public abstract 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; + + /** + * Returns the value to which the specified key is mapped, or null if this map contains no mapping + * for the key. + * + * @param key with which the specified value is to be associated + * @return the value to which the specified key is mapped, or null if this map contains no mapping + * for the key. + * @since 0.16 + */ + @javax.annotation.Nullable + public String get(String key) { + for (Entry entry : getEntries()) { + if (entry.getKey().equals(key)) { + return entry.getValue(); + } + } + return null; + } + + /** + * Returns a {@link List} view of the mappings contained in this {@code TraceState}. + * + * @return a {@link List} view of the mappings contained in this {@code TraceState}. + * @since 0.16 + */ + public abstract List getEntries(); + + /** + * Returns a builder based on this {@code Tracestate}. + * + * @return a builder based on this {@code Tracestate}. + * @since 0.16 + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Builder class for {@link MessageEvent}. + * + * @since 0.16 + */ + @ExperimentalApi + public static final class Builder { + private final Tracestate parent; + @javax.annotation.Nullable private ArrayList entries; + + private static final Tracestate EMPTY = create(Collections.emptyList()); + + public Builder() { + this(EMPTY); + } + + private Builder(Tracestate parent) { + Utils.checkNotNull(parent, "parent"); + this.parent = parent; + this.entries = null; + } + + /** + * Adds or updates the {@code Entry} that has the given {@code key} if it is present. The new + * {@code Entry} will always be added in the front of the list of entries. + * + * @param key the key for the {@code Entry} to be added. + * @param value the value for the {@code Entry} to be added. + * @return this. + * @since 0.16 + */ + @SuppressWarnings("nullness") + public Builder set(String key, String value) { + // Initially create the Entry to validate input. + Entry entry = Entry.create(key, value); + if (entries == null) { + // Copy entries from the parent. + entries = new ArrayList(parent.getEntries()); + } + for (int i = 0; i < entries.size(); i++) { + if (entries.get(i).getKey().equals(entry.getKey())) { + entries.remove(i); + // Exit now because the entries list cannot contain duplicates. + break; + } + } + // Inserts the element at the front of this list. + entries.add(0, entry); + return this; + } + + /** + * Removes the {@code Entry} that has the given {@code key} if it is present. + * + * @param key the key for the {@code Entry} to be removed. + * @return this. + * @since 0.16 + */ + @SuppressWarnings("nullness") + public Builder remove(String key) { + Utils.checkNotNull(key, "key"); + if (entries == null) { + // Copy entries from the parent. + entries = new ArrayList(parent.getEntries()); + } + for (int i = 0; i < entries.size(); i++) { + if (entries.get(i).getKey().equals(key)) { + entries.remove(i); + // Exit now because the entries list cannot contain duplicates. + break; + } + } + return this; + } + + /** + * Builds a TraceState by adding the entries to the parent in front of the key-value pairs list + * and removing duplicate entries. + * + * @return a TraceState with the new entries. + * @since 0.16 + */ + public Tracestate build() { + if (entries == null) { + return parent; + } + return Tracestate.create(entries); + } + } + + /** + * Immutable key-value pair for {@code Tracestate}. + * + * @since 0.16 + */ + @Immutable + @AutoValue + @ExperimentalApi + public abstract static class Entry { + /** + * Creates a new {@code Entry} for the {@code Tracestate}. + * + * @param key the Entry's key. + * @param value the Entry's value. + * @since 0.16 + */ + public static Entry create(String key, String value) { + Utils.checkNotNull(key, "key"); + Utils.checkNotNull(value, "value"); + Utils.checkArgument(validateKey(key), "Invalid key " + key); + Utils.checkArgument(validateValue(value), "Invalid value " + value); + return new AutoValue_Tracestate_Entry(key, value); + } + + /** + * Returns the key {@code String}. + * + * @return the key {@code String}. + * @since 0.16 + */ + public abstract String getKey(); + + /** + * Returns the value {@code String}. + * + * @return the value {@code String}. + * @since 0.16 + */ + public abstract String getValue(); + + Entry() {} + } + + // 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 /. + private static boolean validateKey(String key) { + if (key.length() > KEY_MAX_SIZE + || key.isEmpty() + || key.charAt(0) < 'a' + || key.charAt(0) > 'z') { + return false; + } + for (int i = 1; i < key.length(); i++) { + char c = key.charAt(i); + if (!(c >= 'a' && c <= 'z') + && !(c >= '0' && c <= '9') + && c != '_' + && c != '-' + && c != '*' + && c != '/') { + return false; + } + } + return true; + } + + // Value is opaque string up to 256 characters printable ASCII RFC0020 characters (i.e., the range + // 0x20 to 0x7E) except comma , and =. + private static boolean validateValue(String value) { + if (value.length() > VALUE_MAX_SIZE || value.charAt(value.length() - 1) == ' ' /* '\u0020' */) { + return false; + } + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == ',' || c == '=' || c < ' ' /* '\u0020' */ || c > '~' /* '\u007E' */) { + return false; + } + } + return true; + } + + private static Tracestate create(List entries) { + Utils.checkState(entries.size() <= MAX_KEY_VALUE_PAIRS, "Invalid size"); + return new AutoValue_Tracestate(Collections.unmodifiableList(entries)); + } + + Tracestate() {} +} -- cgit v1.2.3 From b0c487b09287f7734f46d7b84afbd07d6c025f28 Mon Sep 17 00:00:00 2001 From: Bogdan Drutu Date: Fri, 3 Aug 2018 12:37:46 +0300 Subject: Minor improvements for Tracestate. (#1358) --- api/src/main/java/io/opencensus/trace/Tracestate.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'api/src/main/java/io/opencensus/trace/Tracestate.java') diff --git a/api/src/main/java/io/opencensus/trace/Tracestate.java b/api/src/main/java/io/opencensus/trace/Tracestate.java index 63d32fd3..5535a0ac 100644 --- a/api/src/main/java/io/opencensus/trace/Tracestate.java +++ b/api/src/main/java/io/opencensus/trace/Tracestate.java @@ -75,9 +75,19 @@ public abstract class Tracestate { public abstract List getEntries(); /** - * Returns a builder based on this {@code Tracestate}. + * Returns a {@code Builder} based on an empty {@code Tracestate}. * - * @return a builder based on this {@code Tracestate}. + * @return a {@code Builder} based on an empty {@code Tracestate}. + * @since 0.16 + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a {@code Builder} based on this {@code Tracestate}. + * + * @return a {@code Builder} based on this {@code Tracestate}. * @since 0.16 */ public Builder toBuilder() { @@ -94,9 +104,11 @@ public abstract class Tracestate { private final Tracestate parent; @javax.annotation.Nullable private ArrayList entries; + // Needs to be in this class to avoid initialization deadlock because super class depends on + // subclass (the auto-value generate class). private static final Tracestate EMPTY = create(Collections.emptyList()); - public Builder() { + private Builder() { this(EMPTY); } -- cgit v1.2.3 From 52f38e48e2ac6cb65e28dcd97b4f7e9650357bba Mon Sep 17 00:00:00 2001 From: Bogdan Drutu Date: Thu, 9 Aug 2018 10:09:28 -0700 Subject: Add Tracestate into SpanContext. (#1359) * Add Tracestate into SpanContext. * Remove empty constructor from Tracestate.Builder * Add info in the changelog. --- api/src/main/java/io/opencensus/trace/Tracestate.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'api/src/main/java/io/opencensus/trace/Tracestate.java') diff --git a/api/src/main/java/io/opencensus/trace/Tracestate.java b/api/src/main/java/io/opencensus/trace/Tracestate.java index 5535a0ac..f88d3bd3 100644 --- a/api/src/main/java/io/opencensus/trace/Tracestate.java +++ b/api/src/main/java/io/opencensus/trace/Tracestate.java @@ -81,7 +81,7 @@ public abstract class Tracestate { * @since 0.16 */ public static Builder builder() { - return new Builder(); + return new Builder(Builder.EMPTY); } /** @@ -108,10 +108,6 @@ public abstract class Tracestate { // subclass (the auto-value generate class). private static final Tracestate EMPTY = create(Collections.emptyList()); - private Builder() { - this(EMPTY); - } - private Builder(Tracestate parent) { Utils.checkNotNull(parent, "parent"); this.parent = parent; -- cgit v1.2.3 From eabc800c3749ca6f8e3a17f057ae11c8d1385d0c Mon Sep 17 00:00:00 2001 From: Bogdan Drutu Date: Tue, 28 Aug 2018 14:40:27 -0700 Subject: Avoid doing string formatting when calling checkArgument. (#1394) --- api/src/main/java/io/opencensus/trace/Tracestate.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'api/src/main/java/io/opencensus/trace/Tracestate.java') diff --git a/api/src/main/java/io/opencensus/trace/Tracestate.java b/api/src/main/java/io/opencensus/trace/Tracestate.java index f88d3bd3..dae587c8 100644 --- a/api/src/main/java/io/opencensus/trace/Tracestate.java +++ b/api/src/main/java/io/opencensus/trace/Tracestate.java @@ -201,8 +201,8 @@ public abstract class Tracestate { public static Entry create(String key, String value) { Utils.checkNotNull(key, "key"); Utils.checkNotNull(value, "value"); - Utils.checkArgument(validateKey(key), "Invalid key " + key); - Utils.checkArgument(validateValue(value), "Invalid value " + value); + Utils.checkArgument(validateKey(key), "Invalid key %s", key); + Utils.checkArgument(validateValue(value), "Invalid value %s", value); return new AutoValue_Tracestate_Entry(key, value); } -- cgit v1.2.3