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

ffang pushed a commit to branch 4.0.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/4.0.x-fixes by this push:
     new d5b945eb78 [CXF-9162]avoid reusing HttpClientHTTPConduit if the 
underlying HttpC… (#2571)
d5b945eb78 is described below

commit d5b945eb78fc9554ee805029e35e1102dbb3be44
Author: Freeman(Yue) Fang <[email protected]>
AuthorDate: Tue Sep 9 12:28:05 2025 -0400

    [CXF-9162]avoid reusing HttpClientHTTPConduit if the underlying HttpC… 
(#2571)
    
    (cherry picked from commit 19bc70b7eebaa3b0d66c9d68282d8f463171234c)
---
 .../cxf/transport/http/HttpClientHTTPConduit.java  |  32 +++++--
 .../org/apache/cxf/systest/jaxrs/CXF9162Test.java  | 101 +++++++++++++++++++++
 2 files changed, 127 insertions(+), 6 deletions(-)

diff --git 
a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpClientHTTPConduit.java
 
b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpClientHTTPConduit.java
index baf4b49a3b..59a0a977c1 100644
--- 
a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpClientHTTPConduit.java
+++ 
b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpClientHTTPConduit.java
@@ -114,7 +114,7 @@ public class HttpClientHTTPConduit extends 
URLConnectionHTTPConduit {
     private final Queue<RefCount<HttpClient>> deferredClientRefs = new 
ConcurrentLinkedQueue<>();
 
     private static final class RefCount<T extends HttpClient> {
-        private final AtomicLong count = new AtomicLong();
+        private final AtomicLong count;
         private final TLSClientParameters clientParameters;
         private final HTTPClientPolicy policy;
         private final T client;
@@ -125,10 +125,18 @@ public class HttpClientHTTPConduit extends 
URLConnectionHTTPConduit {
             this.policy = policy;
             this.clientParameters = clientParameters;
             this.finalizer = finalizer;
+            this.count = new AtomicLong(1);
         }
 
         RefCount<T> acquire() {
-            count.incrementAndGet();
+            while (true) {
+                final long c = count.get();
+                if (c == 0L) {
+                    throw new IllegalStateException("The client is already 
shutdown");
+                } else if (count.compareAndSet(c, c + 1)) {
+                    break;
+                }
+            }
             return this;
         }
 
@@ -169,6 +177,10 @@ public class HttpClientHTTPConduit extends 
URLConnectionHTTPConduit {
                 }
             }
         }
+        
+        boolean isClosed() {
+            return count.get() == 0L;
+        }
 
         HttpClient client() {
             return client;
@@ -195,14 +207,22 @@ public class HttpClientHTTPConduit extends 
URLConnectionHTTPConduit {
 
             // Do not share if it is not allowed for the conduit or cache 
capacity is exceeded
             if (!shareHttpClient || clients.size() >= MAX_SIZE) {
-                return new RefCount<HttpClient>(supplier.get(), policy, 
clientParameters, () -> { }).acquire();
+                return new RefCount<HttpClient>(supplier.get(), policy, 
clientParameters, () -> { });
             }
 
             lock.lock();
             try {
                 for (final RefCount<HttpClient> p: clients) {
-                    if (cpc.equals(p.policy(), policy) && 
p.clientParameters().equals(clientParameters)) {
-                        return p.acquire();
+                    try {
+                        if (p.isClosed()) { // skip closed but not yet removed 
clients
+                            continue;
+                        }
+                        if (cpc.equals(p.policy(), policy) && 
p.clientParameters().equals(clientParameters)) {
+                            return p.acquire();
+                        }
+                    } catch (final IllegalStateException ex) {
+                        // candidate raced into shutdown; skip and try next
+                        continue;
                     }
                 }
 
@@ -211,7 +231,7 @@ public class HttpClientHTTPConduit extends 
URLConnectionHTTPConduit {
                         () -> this.remove(policy, clientParameters));
                 clients.add(clientRef);
 
-                return clientRef.acquire();
+                return clientRef;
             } finally {
                 lock.unlock();
             }
diff --git 
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/CXF9162Test.java 
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/CXF9162Test.java
new file mode 100644
index 0000000000..c19f697980
--- /dev/null
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/CXF9162Test.java
@@ -0,0 +1,101 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cxf.systest.jaxrs;
+
+import java.util.concurrent.CompletableFuture;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
+import org.apache.cxf.testutil.common.TestUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+
+public class CXF9162Test {
+
+    static final String URL = "http://localhost:"; 
+        + TestUtil.getPortNumber(CXF9162Test.class) + "/";
+
+    static Server server;
+
+    @Before
+    public void startServer() {
+        JAXRSServerFactoryBean serverFactoryBean = new 
JAXRSServerFactoryBean();
+        serverFactoryBean.setResourceClasses(MyServiceImpl.class);
+        serverFactoryBean.setAddress(URL);
+        server = serverFactoryBean.create();
+    }
+
+    @After
+    public void stopServer() {
+        server.stop();
+    }
+
+    @Test
+    public void testMultipleProxyCalls() {
+        JAXRSClientFactoryBean clientFactoryBean = new 
JAXRSClientFactoryBean();
+        clientFactoryBean.setAddress(URL);
+        clientFactoryBean.setResourceClass(MyService.class);
+
+        // Create the first Client and call the RestService
+        MyService myClient1 = clientFactoryBean.create(MyService.class);
+        myClient1.hello();
+
+        // Create a second Client, but do not call yet
+        MyService myClient2 = clientFactoryBean.create(MyService.class);
+        // Register an async GC with finalizer exec and make client1 eligible 
for gc
+        CompletableFuture.runAsync(() -> {
+            System.gc();
+            System.runFinalization();
+            System.gc();
+        });
+        myClient1 = null;
+
+        try {
+            myClient2.hello();
+        } catch (Exception ex) {
+            //the JDK HttpClient may being shutting down
+            assertEquals(ex.getCause().getMessage(), "Client already shutting 
down");
+        }
+
+    }
+
+    @Path("")
+    public interface MyService {
+
+        @GET
+        @Path("/hello")
+        void hello();
+    }
+
+    public static class MyServiceImpl implements MyService {
+
+        @Override
+        public void hello() {
+            System.out.println("hello");
+        }
+    }
+} 

Reply via email to