[ 
https://issues.apache.org/jira/browse/CXF-9234?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Freeman Yue Fang resolved CXF-9234.
-----------------------------------
    Fix Version/s: 4.1.9
                   4.2.4
       Resolution: Fixed

> Concurrent DynamicClientFactory.createClient for the same WSDL url corrupts 
> the cached schema DOM and spins forever at 100% CPU
> -------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CXF-9234
>                 URL: https://issues.apache.org/jira/browse/CXF-9234
>             Project: CXF
>          Issue Type: Bug
>          Components: Simple Frontend
>    Affects Versions: 4.2.2
>         Environment: CXF 4.2.2, Woodstox 7.2.0
> xerces:xercesImpl:2.12.2
> Java Azul 25.0.3
> Arch Linux, also reproduced on Windows 11
>            Reporter: David Richter
>            Assignee: Freeman Yue Fang
>            Priority: Major
>             Fix For: 4.1.9, 4.2.4
>
>         Attachments: cxf-concurrent-schema-clone-repro.zip
>
>
> Two threads calling `DynamicClientFactory.createClient(wsdlUrl, ...)` for the 
> *same* url can enter an unbounded spin at 100% CPU and never return. The 
> thread is RUNNABLE, ignores interrupts, and keeps burning a core until the 
> JVM exits.
> h2. Stacktrace:
>  
> {code:java}
>      java.lang.Thread.State: RUNNABLE
>         at java.util.WeakHashMap.get([email protected]/WeakHashMap.java:416)
>         at org.apache.xerces.dom.CoreDocumentImpl.setUserData(Unknown Source)
>         at org.apache.xerces.dom.NodeImpl.setUserData(Unknown Source)
>         at 
> org.apache.cxf.staxutils.StaxUtils$LocationUserDataHandler.handle(StaxUtils.java:1648)
>         at 
> org.apache.xerces.dom.CoreDocumentImpl.callUserDataHandlers(Unknown Source)
>         at 
> org.apache.xerces.dom.CoreDocumentImpl.callUserDataHandlers(Unknown Source)
>         at org.apache.xerces.dom.NodeImpl.cloneNode(Unknown Source)
>         at org.apache.xerces.dom.ChildNode.cloneNode(Unknown Source)
>         at org.apache.xerces.dom.ParentNode.cloneNode(Unknown Source)
>         at org.apache.xerces.dom.ElementImpl.cloneNode(Unknown Source)
>         at org.apache.xerces.dom.ParentNode.cloneNode(Unknown Source)
>         at org.apache.xerces.dom.ElementImpl.cloneNode(Unknown Source)
>         at org.apache.xerces.dom.ParentNode.cloneNode(Unknown Source)
>         at org.apache.xerces.dom.ElementImpl.cloneNode(Unknown Source)
>         at org.apache.xerces.dom.ParentNode.cloneNode(Unknown Source)
>         at org.apache.xerces.dom.ElementImpl.cloneNode(Unknown Source)
>         at 
> org.apache.cxf.endpoint.dynamic.DynamicClientFactory.cloneNode(DynamicClientFactory.java:986)
>         at 
> org.apache.cxf.endpoint.dynamic.DynamicClientFactory.removeImportElement(DynamicClientFactory.java:946)
>         at 
> org.apache.cxf.endpoint.dynamic.DynamicClientFactory.addSchemas(DynamicClientFactory.java:593)
>         at 
> org.apache.cxf.endpoint.dynamic.DynamicClientFactory.createClient(DynamicClientFactory.java:337)
>         at 
> org.apache.cxf.endpoint.dynamic.DynamicClientFactory.createClient(DynamicClientFactory.java:225)
>  
> ...{code}
> h2. Cause (AI generated during investigation but seems legit):
> 1. `WSDLManagerImpl` caches the parsed `Definition` — and its schema DOM — 
> per WSDL url. Map access is synchronized, but the cached DOM is handed to 
> concurrent callers unsynchronized.
> 2. `DynamicClientFactory.removeImportElement` deep-clones that schema when it 
> contains an `xs:import`/`xs:include` (`DynamicClientFactory:946`; `owner == 
> document`, so this delegates to `node.cloneNode(deep)` on the cached 
> document).
> 3. Every cloned node re-fires `StaxUtils$LocationUserDataHandler.handle` → 
> `dst.setUserData(...)` (`StaxUtils:1643-1650`), writing into the **source** 
> document's userData map.
> 4. That map is not thread safe in any DOM implementation — `WeakHashMap` in 
> Xerces-J, `HashMap` in the JDK DOM — so concurrent clones corrupt it into a 
> cycle and the lookups then loop forever.
> h2.  Reproducer project (AI generated manually checked)
> `cxf-concurrent-schema-clone-repro.zip` — self-contained Maven project, Maven 
> wrapper included, no network service needed.
> {code:java}
> ./mvnw test            # JDK built-in DOM
> ./mvnw test -Pxerces   # Xerces-J 2.12.2
> {code}
> Both fail on `concurrentDeepCloneOfSharedSchemaMustNotHang`, printing the 
> worker stacks to stderr. Select a CXF version with `-Dcxf.version=4.1.4`.
> The project has three tests: the failing one deep-clones a single shared 
> location-tracked schema from N threads (what step 2 above does); a 
> single-threaded control makes the same number of clones in ~4 s, showing the 
> problem is the concurrency and not the schema size; and one exercises the 
> real `createClient` path with two threads on a shared bus — that one is 
> timing dependent and passed on an idle machine here.
> It is a race, so the failing test retries fresh rounds for up to 180 s before 
> giving up (it tripped on round 1–3 on a 22-core box). 
> `observed-thread-dumps.txt` in the archive holds real dumps from both DOM 
> variants in case it does not trip on your hardware.
> Note: once it trips, the worker threads cannot be stopped — expect to kill 
> the JVM.
> h2. Workaround
>   Build each client on its own Bus, so the per-bus WSDLManager cache - and 
> therefore the
>   schema DOM - is not shared between threads.



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

Reply via email to