Skip to content

Fix NPE in XMLHandlerImpl.dispose caused by concurrent DOM serialization#91

Merged
vharseko merged 1 commit into
masterfrom
copilot/fix-null-pointer-exception
May 14, 2026
Merged

Fix NPE in XMLHandlerImpl.dispose caused by concurrent DOM serialization#91
vharseko merged 1 commit into
masterfrom
copilot/fix-null-pointer-exception

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 14, 2026

Problem

OpenICFProvisionerService.getStatus → connector test()XMLConnector.dispose produced:

SEVERE: Cannot invoke "org.w3c.dom.Node.getNodeType()" because "child" is null
java.lang.NullPointerException
    at net.sf.saxon.dom.DOMSender.walkNode(DOMSender.java:142)
    ...
    at net.sf.saxon.IdentityTransformer.transform(IdentityTransformer.java:39)
    at org.forgerock.openicf.connectors.xml.XMLHandlerImpl.dispose(XMLHandlerImpl.java:431)
    at org.forgerock.openicf.connectors.xml.ConcurrentXMLHandler.dispose(ConcurrentXMLHandler.java:132)
    at org.forgerock.openicf.connectors.xml.XMLConnector.dispose(XMLConnector.java:111)

Root cause

ConcurrentXMLHandler.init()/dispose() mutated the invokers reference counter and called proxy.dispose() under a shared read lock. Two threads could simultaneously decrement to zero and run the DOM serialization in parallel on the same Document. Saxon's DOMSender walks NodeList.item(i) after getLength(); a concurrent removeChild (from the empty-text-node cleanup or another in-flight transform) makes item(i) return null for a still-in-range index, producing the NPE.

Fix

  1. ConcurrentXMLHandler.init() / dispose() now use the write lock, so the lifecycle counter and proxy.dispose() cannot race against each other or against search/authenticate read-lock walks of the same DOM.
  2. XMLHandlerImpl.dispose() is hardened:
    • The empty-text-node NodeList is snapshotted into a List<Node> before mutating the DOM.
    • Parent nodes are null-checked before removeChild.
    • The XPath cleanup + Saxon transform is wrapped in synchronized (document) as defense-in-depth.
    • The FileOutputStream is now closed in a finally block.
  3. XMLConnector.dispose() already nulls out xmlInstanceHandler after disposal, so a second dispose() from upper layers is already a safe no-op (no change needed).

Validation

  • mvn -ntp test in OpenICF-xml-connector: 81/81 tests pass.
  • CodeQL: 0 alerts.

Fixes the Cannot invoke "org.w3c.dom.Node.getNodeType()" because "child" is null reported in the issue.

Copilot AI requested a review from vharseko May 14, 2026 12:14
@vharseko vharseko requested review from maximthomas and removed request for vharseko May 14, 2026 12:15
@vharseko vharseko marked this pull request as ready for review May 14, 2026 12:15
@vharseko vharseko merged commit 1bbdf23 into master May 14, 2026
17 checks passed
@vharseko vharseko deleted the copilot/fix-null-pointer-exception branch May 14, 2026 13:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants