diff --git a/aws-xray-propagator/build.gradle.kts b/aws-xray-propagator/build.gradle.kts
index 63a869baa..356267d64 100644
--- a/aws-xray-propagator/build.gradle.kts
+++ b/aws-xray-propagator/build.gradle.kts
@@ -10,4 +10,8 @@ otelJava.moduleName.set("io.opentelemetry.contrib.awsxray.propagator")
dependencies {
api("io.opentelemetry:opentelemetry-api")
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
+ testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
+ testImplementation("io.opentelemetry:opentelemetry-sdk-trace")
+ testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
+ testImplementation("uk.org.webcompere:system-stubs-jupiter:2.0.2")
}
diff --git a/aws-xray-propagator/src/main/java/io/opentelemetry/contrib/awsxray/propagator/AwsXrayLambdaConfigurablePropagator.java b/aws-xray-propagator/src/main/java/io/opentelemetry/contrib/awsxray/propagator/AwsXrayLambdaConfigurablePropagator.java
new file mode 100644
index 000000000..57e030b4a
--- /dev/null
+++ b/aws-xray-propagator/src/main/java/io/opentelemetry/contrib/awsxray/propagator/AwsXrayLambdaConfigurablePropagator.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.contrib.awsxray.propagator;
+
+import io.opentelemetry.context.propagation.TextMapPropagator;
+import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
+import io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider;
+
+/**
+ * A {@link ConfigurablePropagatorProvider} which allows enabling the {@link
+ * AwsXrayLambdaPropagator} with the propagator name {@code xray-lambda}.
+ */
+public final class AwsXrayLambdaConfigurablePropagator implements ConfigurablePropagatorProvider {
+ @Override
+ public TextMapPropagator getPropagator(ConfigProperties config) {
+ return AwsXrayLambdaPropagator.getInstance();
+ }
+
+ @Override
+ public String getName() {
+ return "xray-lambda";
+ }
+}
diff --git a/aws-xray-propagator/src/main/java/io/opentelemetry/contrib/awsxray/propagator/AwsXrayLambdaPropagator.java b/aws-xray-propagator/src/main/java/io/opentelemetry/contrib/awsxray/propagator/AwsXrayLambdaPropagator.java
new file mode 100644
index 000000000..f09f8163a
--- /dev/null
+++ b/aws-xray-propagator/src/main/java/io/opentelemetry/contrib/awsxray/propagator/AwsXrayLambdaPropagator.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.contrib.awsxray.propagator;
+
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.propagation.TextMapGetter;
+import io.opentelemetry.context.propagation.TextMapPropagator;
+import io.opentelemetry.context.propagation.TextMapSetter;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nullable;
+
+/**
+ * Implementation of the AWS X-Ray Trace Header propagation protocol but with special handling for
+ * Lambda's {@code _X_AMZN_TRACE_ID} environment variable and {@code com.amazonaws.xray.traceHeader}
+ * system property.
+ *
+ *
To register the X-Ray propagator together with default propagator when using the SDK:
+ *
+ *
{@code
+ * OpenTelemetrySdk.builder()
+ * .setPropagators(
+ * ContextPropagators.create(
+ * TextMapPropagator.composite(
+ * W3CTraceContextPropagator.getInstance(),
+ * AwsXrayLambdaPropagator.getInstance())))
+ * .build();
+ * }
+ */
+public final class AwsXrayLambdaPropagator implements TextMapPropagator {
+
+ private static final String AWS_TRACE_HEADER_ENV_KEY = "_X_AMZN_TRACE_ID";
+ private static final String AWS_TRACE_HEADER_PROP = "com.amazonaws.xray.traceHeader";
+ private final AwsXrayPropagator xrayPropagator = AwsXrayPropagator.getInstance();
+ private static final AwsXrayLambdaPropagator INSTANCE = new AwsXrayLambdaPropagator();
+
+ private AwsXrayLambdaPropagator() {
+ // singleton
+ }
+
+ public static AwsXrayLambdaPropagator getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public List fields() {
+ return xrayPropagator.fields();
+ }
+
+ @Override
+ public void inject(Context context, @Nullable C carrier, TextMapSetter setter) {
+ xrayPropagator.inject(context, carrier, setter);
+ }
+
+ @Override
+ public Context extract(Context context, @Nullable C carrier, TextMapGetter getter) {
+ Context xrayContext = xrayPropagator.extract(context, carrier, getter);
+
+ if (Span.fromContext(context).getSpanContext().isValid()) {
+ return xrayContext;
+ }
+
+ String traceHeader = System.getProperty(AWS_TRACE_HEADER_PROP);
+ if (isEmptyOrNull(traceHeader)) {
+ traceHeader = System.getenv(AWS_TRACE_HEADER_ENV_KEY);
+ }
+ if (isEmptyOrNull(traceHeader)) {
+ return xrayContext;
+ }
+ return xrayPropagator.extract(
+ xrayContext,
+ Collections.singletonMap(AwsXrayPropagator.TRACE_HEADER_KEY, traceHeader),
+ MapGetter.INSTANCE);
+ }
+
+ private static boolean isEmptyOrNull(@Nullable String value) {
+ return value == null || value.isEmpty();
+ }
+
+ private enum MapGetter implements TextMapGetter