David Richter created CXF-9234:
----------------------------------

             Summary: 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
         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