This is an automated email from the ASF dual-hosted git repository.

andy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git

commit cb40eaee8c57f3bf3d81790a040b96b85c26ed7a
Author: Andy Seaborne <[email protected]>
AuthorDate: Fri Mar 6 15:45:30 2026 +0000

    GH-3786: Set the default HttpClient only at runtime
---
 .../main/java/org/apache/jena/http/HttpEnv.java    | 29 +++++++++++++++++++---
 .../main/java/org/apache/jena/riot/RDFParser.java  |  5 ++--
 .../org/apache/jena/fuseki/main/TestQuery.java     |  2 +-
 3 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/jena-arq/src/main/java/org/apache/jena/http/HttpEnv.java 
b/jena-arq/src/main/java/org/apache/jena/http/HttpEnv.java
index 433bd5b2ff..133b1f07f8 100644
--- a/jena-arq/src/main/java/org/apache/jena/http/HttpEnv.java
+++ b/jena-arq/src/main/java/org/apache/jena/http/HttpEnv.java
@@ -45,27 +45,48 @@ public class HttpEnv {
      */
     public static /* final */ int urlLimit = 2 * 1024;
 
-    public static HttpClient getDftHttpClient() { return httpClient; }
+    public static HttpClient getDftHttpClient() { return 
getBuildDftHttpClient(); }
     public static void setDftHttpClient(HttpClient dftHttpClient) { httpClient 
= dftHttpClient; }
 
     /** Return the {@link HttpClient} based on URL and a possible pre-selected 
{@link HttpClient}. */
     public static HttpClient getHttpClient(String url, HttpClient 
specificHttpClient) {
         if ( specificHttpClient != null )
              return specificHttpClient;
+        return getHttpClient(url);
+    }
+
+    /** Return the {@link HttpClient} based on URL or the system default 
({@link HttpEnv#getDftHttpClient}). */
+    public static HttpClient getHttpClient(String url) {
         HttpClient requestHttpClient = RegistryHttpClient.get().find(url);
         if ( requestHttpClient == null )
             requestHttpClient = getDftHttpClient();
         return requestHttpClient;
     }
 
-    private static HttpClient httpClient = buildDftHttpClient();
+    // Delay initialization until runtime even if doing GraalVM native code of 
Java AOT caching.
+    // The HTTP subsystem must be initialized at runtime.
+    // The LazyHolder pattern (Bill Pugh singleton pattern) does not work, The 
class
+    // is findable by class analysis and a candidate for build-time execution.
+    //
+    // An AtomicReference could be used if there was a Supplier version of
+    // compareAndSet (c.f ConcurrentHashMap.computeIfAbsent).
+
+    private static volatile HttpClient httpClient = null;
 
-    private static HttpClient buildDftHttpClient() {
-        return httpClientBuilder().build();
+    // Get httpClient, building it (at runtime) if it is not initialized.
+    private static HttpClient getBuildDftHttpClient() {
+        if ( httpClient == null ) {
+            synchronized(HttpEnv.class) {
+                if ( httpClient == null )
+                    httpClient = httpClientBuilder().build();
+            }
+        }
+        return httpClient;
     }
 
     public static final String UserAgent = ARQ.VERSION.contains("devel") ? 
"ApacheJena" : "ApacheJena/"+ARQ.VERSION;
 
+    /** Build an {@link HttpClient}, which follows redirects for https-http 
redirects */
     public static HttpClient.Builder httpClientBuilder() {
         return HttpClient.newBuilder()
                 // By default, the client has polling and connection-caching.
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/RDFParser.java 
b/jena-arq/src/main/java/org/apache/jena/riot/RDFParser.java
index 1ddde04c25..1f90e8fd1b 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/RDFParser.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/RDFParser.java
@@ -438,7 +438,8 @@ public class RDFParser {
         }
 
         TypedInputStream in;
-        // Need more control than LocatorURL provides to get the Accept header 
in and the HttpCLient.
+        // Need more control than LocatorURL provides to get the Accept header
+        // in and the HttpCLient setup.
         // So map now.
         urlStr = streamManager.mapURI(urlStr);
         if ( urlStr.startsWith("http://";) || urlStr.startsWith("https://";) ) {
@@ -450,7 +451,7 @@ public class RDFParser {
                 b.setHeader(HttpNames.hAccept, acceptHeader);
             });
             // Setup of the HTTP client, if not provided by RDFParserBuilder
-            final var httpClientToUse = ( httpClient != null ) ? httpClient : 
HttpEnv.getDftHttpClient();
+            final var httpClientToUse = ( httpClient != null ) ? httpClient : 
HttpEnv.getHttpClient(urlStr);
             HttpResponse<InputStream> response = 
HttpLib.execute(httpClientToUse, request);
             in = HttpLib.handleResponseTypedInputStream(response);
         } else {
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestQuery.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestQuery.java
index 24d39e5058..693d7bf5da 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestQuery.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestQuery.java
@@ -274,7 +274,7 @@ public class TestQuery extends AbstractFusekiTest {
 
     @Test
     public void query_describe_conneg() throws IOException {
-        HttpClient client = HttpEnv.httpClientBuilder().build();
+        HttpClient client = HttpEnv.getDftHttpClient();
         String query = "DESCRIBE ?s WHERE {?s ?p ?o}";
         for (MediaType type : rdfOfferTest.entries()) {
             String contentType = type.toHeaderString();

Reply via email to