From 4267202f8fb279a2e7e4f6579e999c450fbcddae Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Fri, 2 Jul 2021 11:35:47 +0200 Subject: [PATCH] Fix #3364 Signed-off-by: Francesco Guardiani --- .../CompositeTextMapPropagatorBuilder.java | 69 +++++++++++++++++++ .../propagation/MultiTextMapPropagator.java | 24 +++++-- .../propagation/TextMapPropagator.java | 7 ++ .../MultiTextMapPropagatorTest.java | 29 ++++++++ 4 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 context/src/main/java/io/opentelemetry/context/propagation/CompositeTextMapPropagatorBuilder.java diff --git a/context/src/main/java/io/opentelemetry/context/propagation/CompositeTextMapPropagatorBuilder.java b/context/src/main/java/io/opentelemetry/context/propagation/CompositeTextMapPropagatorBuilder.java new file mode 100644 index 00000000000..5a26ed91217 --- /dev/null +++ b/context/src/main/java/io/opentelemetry/context/propagation/CompositeTextMapPropagatorBuilder.java @@ -0,0 +1,69 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.context.propagation; + +import java.util.ArrayList; +import java.util.List; + +/** + * A builder for configuring an {@link TextMapPropagator} specifying which propagators should be used for extracting the context and which should be used for injecting the context. + */ +public final class CompositeTextMapPropagatorBuilder { + + private final List extractors; + private final List injectors; + + /** + * Package protected to disallow direct initialization. + * + * @see TextMapPropagator#builder() + */ + CompositeTextMapPropagatorBuilder() { + this.extractors = new ArrayList<>(); + this.injectors = new ArrayList<>(); + } + + /** + * Add a {@link TextMapPropagator} to be used only to extract the context. + */ + public CompositeTextMapPropagatorBuilder extractor(TextMapPropagator propagator) { + this.extractors.add(propagator); + return this; + } + + /** + * Add a {@link TextMapPropagator} to be used only to inject the context. + */ + public CompositeTextMapPropagatorBuilder injector(TextMapPropagator propagator) { + this.injectors.add(propagator); + return this; + } + + /** + * Add a {@link TextMapPropagator} to be used both to extract and inject the context. + */ + public CompositeTextMapPropagatorBuilder propagator(TextMapPropagator propagator) { + this.injectors.add(propagator); + this.extractors.add(propagator); + return this; + } + + /** + * Returns the built {@link TextMapPropagator} + * + * @see CompositeTextMapPropagatorBuilder + */ + public TextMapPropagator build() { + if (this.injectors.isEmpty()) { + this.injectors.add(TextMapPropagator.noop()); + } + if (this.extractors.isEmpty()) { + this.extractors.add(TextMapPropagator.noop()); + } + + return new MultiTextMapPropagator(this.extractors, this.injectors); + } +} diff --git a/context/src/main/java/io/opentelemetry/context/propagation/MultiTextMapPropagator.java b/context/src/main/java/io/opentelemetry/context/propagation/MultiTextMapPropagator.java index ce2cecb165c..f113e5dce35 100644 --- a/context/src/main/java/io/opentelemetry/context/propagation/MultiTextMapPropagator.java +++ b/context/src/main/java/io/opentelemetry/context/propagation/MultiTextMapPropagator.java @@ -16,7 +16,8 @@ import javax.annotation.Nullable; final class MultiTextMapPropagator implements TextMapPropagator { - private final TextMapPropagator[] textPropagators; + private final TextMapPropagator[] extractors; + private final TextMapPropagator[] injectors; private final Collection allFields; MultiTextMapPropagator(TextMapPropagator... textPropagators) { @@ -24,9 +25,20 @@ final class MultiTextMapPropagator implements TextMapPropagator { } MultiTextMapPropagator(List textPropagators) { - this.textPropagators = new TextMapPropagator[textPropagators.size()]; - textPropagators.toArray(this.textPropagators); - this.allFields = Collections.unmodifiableList(getAllFields(this.textPropagators)); + this.extractors = new TextMapPropagator[textPropagators.size()]; + textPropagators.toArray(this.extractors); + this.injectors = this.extractors; // No need to copy + this.allFields = Collections.unmodifiableList(getAllFields(this.injectors)); + } + + MultiTextMapPropagator(List extractors, List injectors) { + this.extractors = new TextMapPropagator[extractors.size()]; + extractors.toArray(this.extractors); + + this.injectors = new TextMapPropagator[injectors.size()]; + injectors.toArray(this.injectors); + + this.allFields = Collections.unmodifiableList(getAllFields(this.injectors)); } @Override @@ -48,7 +60,7 @@ public void inject(Context context, @Nullable C carrier, TextMapSetter se if (context == null || setter == null) { return; } - for (TextMapPropagator textPropagator : textPropagators) { + for (TextMapPropagator textPropagator : injectors) { textPropagator.inject(context, carrier, setter); } } @@ -61,7 +73,7 @@ public Context extract(Context context, @Nullable C carrier, TextMapGetter carrier = new HashMap<>(); + + TextMapPropagator prop = TextMapPropagator.builder() + .injector(propagator1) + .extractor(propagator2) + .propagator(propagator3) + .build(); + + Context context1 = mock(Context.class); + Context context2 = mock(Context.class); + Context expectedContext = mock(Context.class); + + when(propagator2.extract(context1, carrier, getter)).thenReturn(context2); + when(propagator3.extract(context2, carrier, getter)).thenReturn(expectedContext); + + assertThat(prop.extract(context1, carrier, getter)).isEqualTo(expectedContext); + + Context context = mock(Context.class); + TextMapSetter> setter = Map::put; + + prop.inject(context, carrier, setter); + verify(propagator1).inject(context, carrier, setter); + verify(propagator2, never()).inject(context, carrier, setter); + verify(propagator3).inject(context, carrier, setter); + } }