Search code examples
javaxmlparsingxercesjava-11

NodeList.getLength() returning 0 in Java 11


I am migrating my application from Java 8 to Java 11. In Java 8, my input xml file was parsing correctly, but in Java 11 the nodeList.getLength() is returning 0.
Please refer the code as per which I am always getting the error: Cannot find signature.

I have tried using document.getElementsByTagName("Signature") instead of getElementsByTagNameNS(), which returns correct length. However the unmarshalXMLSignature() method then gives an error that Document implementation must support DOM Level 2 and be namespace aware.
Is there any challenge with using getElementsByTagNameNS() method in Java 11?

private DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
. . .
document = documentBuilderFactory.newDocumentBuilder().parse(input);
final NodeList nodeList = document.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nodeList.getLength() == 0) {
    throw new SecurityException("Cannot find signature");
}
final XMLValidateContext validateContext = new DOMValidateContext(newX509KeySelector(), nodeList.item(0));
final XMLSignature signature = getXMLSignatureFactory().unmarshalXMLSignature(validateContext);
. . .

Edit 1: The input xml is

    <?xml version="1.0" encoding="UTF-8" standalone="no"?><License>
        <id>999</id>
        <companyName>TRXX</companyName>
        <creationDate>01-01-2016</creationDate>
        <expirationDate>never</expirationDate>
        <features>
            <feature>
                <name>drwwy</name>
                <value>true</value>
                <description>drwwy module</description>
            </feature>
            <feature>
                <name>pnp</name>
                <value>true</value>
                <description>Index Number Management module</description>
            </feature>
        </features>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI="">
<Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>l5Cl4E=</DigestValue></Reference></SignedInfo><SignatureValue>R3zBkE=</SignatureValue>
<KeyInfo><X509Data><X509SubjectName>CN=TRXX Inc,OU=Product Development,O=TRXX Inc,L=Scotle,ST=Atlanta,C=US</X509SubjectName><X509Certificate>MIY</X509Certificate>
</X509Data></KeyInfo></Signature></License>

Edit 2: I figured out, earlier I was injecting DocumentBuilderFactory which is part of JDK , but it is no longer allowed in Java 11. That is why I instantiated it using DocumentBuilderFactory.newInstance(), but with this one my nodelist length comes as zero. Can we use any other way to instantiate DocumentBuilderFactory object ?


Solution

  • If you use document.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); you get an empty nodeList and the source code provided throws your new SecurityException("Cannot find signature").

    If you document.getElementsByTagName("Signature"), the Signature element is found but without namespace awareness, throwing this exception later on:

    javax.xml.crypto.MarshalException: Document implementation must support DOM Level 2 and be namespace aware
      at org.jcp.xml.dsig.internal.dom.DOMXMLSignatureFactory.unmarshal(DOMXMLSignatureFactory.java:189)
      at org.jcp.xml.dsig.internal.dom.DOMXMLSignatureFactory.unmarshalXMLSignature(DOMXMLSignatureFactory.java:150)
    

    When researching the term getElementsByTagNameNS(XMLSignature.XMLNS, "Signature") I found code examples where they activate namespace awareness on the new instance of DocumentBuilderFactory manually:

    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
    documentBuilderFactory.setNamespaceAware(true);
    

    This should fix your new SecurityException("Cannot find signature") issue.

    Regarding your previous question you not only migrated Java 8 to 11, but also CDI API, OpenWebBeans, Apache Tomcat and maybe other modules too, someone else must have done this activation of namespace awareness for you previously. Or saying it differently - you @Injected a namespace aware DocumentBuilderFactory instance.

    Edit:

    For manual configuration and injection of DocumentBuilderFactory, create a method that @Produces an instance of the DocumentBuilderFactory and provides that instance for injection elsewhere:

    import javax.enterprise.context.ApplicationScoped;
    import javax.enterprise.context.Dependent;
    import javax.enterprise.inject.Produces;
    import javax.xml.parsers.DocumentBuilderFactory;
    
    @Dependent
    public class DocumentBuilderFactoryProducer {
        @Produces
        @ApplicationScoped
        public DocumentBuilderFactory createDocumentBuilderFactory() {
            DocumentBuilderFactory result = DocumentBuilderFactory.newInstance();
            result.setNamespaceAware(true);
            // other customizations ...
            return result;
        }
    }
    

    This can be injected into other beans like so:

    @Inject
    private DocumentBuilderFactory documentBuilderFactory;