1313import io .opentelemetry .sdk .common .CompletableResultCode ;
1414import io .opentelemetry .sdk .common .export .ProxyOptions ;
1515import io .opentelemetry .sdk .common .export .RetryPolicy ;
16+ import io .opentelemetry .sdk .internal .DaemonThreadFactory ;
1617import java .io .ByteArrayOutputStream ;
1718import java .io .IOException ;
1819import java .io .OutputStream ;
3233import java .util .concurrent .CompletableFuture ;
3334import java .util .concurrent .ConcurrentLinkedQueue ;
3435import java .util .concurrent .ExecutorService ;
36+ import java .util .concurrent .SynchronousQueue ;
3537import java .util .concurrent .ThreadLocalRandom ;
38+ import java .util .concurrent .ThreadPoolExecutor ;
3639import java .util .concurrent .TimeUnit ;
3740import java .util .function .Consumer ;
3841import java .util .function .Predicate ;
@@ -72,6 +75,23 @@ public final class JdkHttpSender implements HttpSender {
7275 @ Nullable private final RetryPolicy retryPolicy ;
7376 private final Predicate <IOException > retryExceptionPredicate ;
7477
78+ /**
79+ * Returns an {@link ExecutorService} using daemon threads.
80+ *
81+ * @param propagateContextForTesting For tests only. When enabled, the current thread's Context
82+ * will be passed over to the new threads, this is useful for validating scenarios where
83+ * context propagation is available through bytecode instrumentation.
84+ */
85+ public static ExecutorService newExecutor (boolean propagateContextForTesting ) {
86+ return new ThreadPoolExecutor (
87+ 0 ,
88+ Integer .MAX_VALUE ,
89+ 60 ,
90+ TimeUnit .SECONDS ,
91+ new SynchronousQueue <>(),
92+ new DaemonThreadFactory ("jdkhttp-executor" , propagateContextForTesting ));
93+ }
94+
7595 // Visible for testing
7696 JdkHttpSender (
7797 HttpClient client ,
@@ -82,7 +102,8 @@ public final class JdkHttpSender implements HttpSender {
82102 long timeoutNanos ,
83103 Supplier <Map <String , List <String >>> headerSupplier ,
84104 @ Nullable RetryPolicy retryPolicy ,
85- @ Nullable ExecutorService executorService ) {
105+ @ Nullable ExecutorService executorService ,
106+ boolean propagateContextForTesting ) {
86107 this .client = client ;
87108 try {
88109 this .uri = new URI (endpoint );
@@ -100,7 +121,7 @@ public final class JdkHttpSender implements HttpSender {
100121 .map (RetryPolicy ::getRetryExceptionPredicate )
101122 .orElse (JdkHttpSender ::isRetryableException );
102123 if (executorService == null ) {
103- this .executorService = JdkHtttpUtil .newExecutor ();
124+ this .executorService = JdkHttpSender .newExecutor (propagateContextForTesting );
104125 this .managedExecutor = true ;
105126 } else {
106127 this .executorService = executorService ;
@@ -129,7 +150,8 @@ public final class JdkHttpSender implements HttpSender {
129150 timeoutNanos ,
130151 headerSupplier ,
131152 retryPolicy ,
132- executorService );
153+ executorService ,
154+ /* propagateContextForTesting= */ false );
133155 }
134156
135157 private static HttpClient configureClient (
@@ -223,7 +245,8 @@ HttpResponse<byte[]> sendInternal(Marshaler marshaler) throws IOException {
223245 Thread .currentThread ().interrupt ();
224246 break ; // Break out and return response or throw
225247 }
226- // If after sleeping we've exceeded timeoutNanos, break out and return response or throw
248+ // If after sleeping we've exceeded timeoutNanos, break out and return
249+ // response or throw
227250 if ((System .nanoTime () - startTimeNanos ) >= timeoutNanos ) {
228251 break ;
229252 }
@@ -304,12 +327,15 @@ private HttpResponse<byte[]> sendRequest(
304327 }
305328
306329 private static boolean isRetryableException (IOException throwable ) {
307- // Almost all IOExceptions we've encountered are transient retryable, so we opt out of specific
330+ // Almost all IOExceptions we've encountered are transient retryable, so we
331+ // opt out of specific
308332 // IOExceptions that are unlikely to resolve rather than opting in.
309- // Known retryable IOException messages: "Connection reset", "/{remote ip}:{remote port} GOAWAY
333+ // Known retryable IOException messages: "Connection reset", "/{remote
334+ // ip}:{remote port} GOAWAY
310335 // received"
311336 // Known retryable HttpTimeoutException messages: "request timed out"
312- // Known retryable HttpConnectTimeoutException messages: "HTTP connect timed out"
337+ // Known retryable HttpConnectTimeoutException messages: "HTTP connect timed
338+ // out"
313339 return !(throwable instanceof SSLException );
314340 }
315341
0 commit comments