[ 
https://issues.apache.org/jira/browse/CXF-9162?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18016130#comment-18016130
 ] 

Andriy Redko commented on CXF-9162:
-----------------------------------

Thanks [~ffang] , I think we don't need `shuttingDown` - it introduces another 
race condition (and second variable), we should be able to use AtomicLong 
semantics in order to fix that. 

> Reusing a Client with a HttpClientHTTPConduit is not threadsafe in regards to 
> finalization
> ------------------------------------------------------------------------------------------
>
>                 Key: CXF-9162
>                 URL: https://issues.apache.org/jira/browse/CXF-9162
>             Project: CXF
>          Issue Type: Bug
>            Reporter: Eric
>            Assignee: Freeman Yue Fang
>            Priority: Major
>             Fix For: 4.1.4, 3.6.9, 4.0.10
>
>
> If a RestClient is created with the default HttpClientHTTPConduit it reuses 
> the client by default.
> This, however, is not threadsafe in all cases, which is especially 
> problematic when the client is implicitly closed by the finalizer. Consider 
> the following example:
> {code:java}
> public class ResourceProblemWithCxfClientWithHttpClientHTTPConduitTest {
>     static final String URL = "http://localhost:8080/";;
>     static Server server;
>     @BeforeAll
>     static void startServer() {
>         var serverFactoryBean = new JAXRSServerFactoryBean();
>         serverFactoryBean.setResourceClasses(MyServiceImpl.class);
>         serverFactoryBean.setAddress(URL);
>         server = serverFactoryBean.create();
>     }
>     @AfterAll
>     static void stopServer() {
>         server.stop();
>     }
>     @Test
>     void testMultipleProxyCalls() {
>         var clientFactoryBean = new JAXRSClientFactoryBean();
>         clientFactoryBean.setAddress(URL);
>         clientFactoryBean.setResourceClass(MyService.class);
>         // Create the first Client and call the RestService
>         var myClient1 = clientFactoryBean.create(MyService.class);
>         myClient1.hello();
>         // Create a second Client, but do not call yet
>         var 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();
>             System.out.println("GC'D");
>         });
>         myClient1 = null;
>         // Now call the second client:
>         // -- with Java17, the method very often just blocks forever
>         // -- with Java21, it might throw the following exception:
>         // jakarta.ws.rs.ProcessingException: java.io.IOException: 
> IOException invoking http://localhost:8080/hello:
>         // shutdownNow
>         myClient2.hello();
>         // the reason behind seems to be wether the resource-aquiciring of 
> the shared client collides
>         // with the cleanup of the first in the finalizer
>     }
>     @Path("")
>     public interface MyService {
>         @GET
>         @Path("/hello")
>         void hello();
>     }
>     public static class MyServiceImpl implements MyService {
>         @Override
>         public void hello() {
>             System.out.println("hello");
>         }
>     }
> } {code}
>  
> When everything works fine, this code prints the following:
> {noformat}
> hello
> GC'D
> hello{noformat}
>  
> In many cases, however, it does not, because the GC collects the first client 
> while second tries to acquire the ressource. Here, the garbage collector is 
> aggressively invoked, but this has happend to us in production as well if the 
> GC thinks its cleanup time while the second webclient-invocation starts.
> In the error case, the following can happen:
>  * On Java 17, the first two statements are printed, then the test hangs 
> forever
>  * On Java 21+ which invokes close on the httpclient, all statments are 
> printed followed by an IOException: 
> {noformat}
> 17:35:13.284 [main] WARN org.apache.cxf.phase.PhaseInterceptorChain -- 
> Interceptor for 
> {http://rest.itest.appkit.platform.arc.finkonsens.de/}MyService has thrown 
> exception, unwinding now
> org.apache.cxf.interceptor.Fault: Could not send Message.{noformat}
>  
> This error always happens when the gc finalizer closes the last running 
> httpclient while a new call tries to acquired the shared client. If you 
> cannot reproduce it with the test case above, then just run it in debug mode 
> and set the following breakpoint with a hit count of 2 and blocking only the 
> active thread, not all threads:
> {noformat}
> HttpClientHTTPConduit.RefCount.acquire{noformat}
> This should block the acquire long enough so that the error will always 
> happen.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to