0

I'm working on a webservice that uses SOAP as a communication protocol: I achieve this by using Spring Boot and Spring WS.
Sometimes, it may happen that I need to send some stuff through the request's header and I want to be able to recover that information via an XPath expression.
Here's what I did so far:

        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        WebServiceMessage message = messageContext.getRequest();

        Source s = messageContext.getRequest().getPayloadSource();
        messageContext.getRequest().writeTo(buffer);
        String payload = buffer.toString(java.nio.charset.StandardCharsets.UTF_8.name());
        System.out.println(payload);
        InputStream is = new ByteArrayInputStream(payload.getBytes());

In the following code snippet I read the whole request that get back-end receives. Afterwards, what I used to do was to transform all of this in a SOAPHeader object, that held everything that I wanted.... except for the fact that I couldn't make any XPath query at all and the element I needed where really down the tree.
So, I decided to continue that snippet of code in the following way

        //-------
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new java.io.ByteArrayInputStream(payload.getBytes()));
        XPath xpath = XPathFactory.newInstance().newXPath();

Now, this is a sample of the request I'm sending over

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:it="it.niuma.mscsoapws.ws">
   <soapenv:Header>
       <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
       <wsse:UsernameToken wsu:Id="UsernameToken-3967AEB46D733EF6E2154990461080350">
       <wsse:Username>TAVI</wsse:Username>
       <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
       <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">pUn8VjdpVaIamSAIwXEeXg==</wsse:Nonce>
       <wsu:Created>2019-02-11T17:03:30.803Z</wsu:Created>
       </wsse:UsernameToken></wsse:Security>
  </soapenv:Header>
   <soapenv:Body>
      <it:getPOrderRequest>
         <it:poNumber>2197111225-F03292</it:poNumber>
      </it:getPOrderRequest>
   </soapenv:Body>
</soapenv:Envelope>

What I want to extrapolate is the entire part inside soapenv:Header, so I use the following XPath Expression:

        XPathExpression expr = xpath.compile("//*//soapenv:Header");
        Object result = expr.evaluate(doc, XPathConstants.NODESET);
        NodeList nodes = (NodeList) result;
        for (int i = 0; i < nodes.getLength(); i++) {
            Node currentItem = nodes.item(i);
            System.out.println("found node -> " + currentItem.getLocalName() + " (namespace: " + currentItem.getNamespaceURI() + ")");
        }

Here's my problem: the XPath expression, Java side, doesn't return anything while other tools (like this one) evaluates the expression correctly and return something. The only XPath expression that gets correctly evaluated in Java is //*, nothing else. What am I missing?

EDIT: After @Wisthler's suggestion, the XPath is now evaluated but it fails to return something. Attaching a screenshot of what happens now: see how the nodes variable (which should contain a NodesList) is, if I understood correctly, empty

enter image description here

Gianmarco F.
  • 780
  • 2
  • 12
  • 36
  • please refer https://stackoverflow.com/a/13702843/8700934 – Lakshan Feb 12 '19 at 15:32
  • @Rathnayake if you look the code that I've posted it's half inspired from that resource but it still won't return nothing even after adding the namespaceuri part – Gianmarco F. Feb 12 '19 at 15:33

2 Answers2

0

I know last time I played with XPath in java I had troubles with namespaces and had to add a namespaceContext to make it work.

you need to "register" the soapenv namespace if you want to use it in the XPath. You can find below what I did back then. Probably not 100% clean but might still help you

    xpath.setNamespaceContext(new NamespaceContext() {
        @Override
        public String getNamespaceURI(String prefix) {
            if("soapenv".equals(prefix)){
                return "http://schemas.xmlsoap.org/soap/envelope/";
            }else{
                return null;
            }
        }

        @Override
        public String getPrefix(String namespaceURI) {
            return null;
        }

        @Override
        public Iterator getPrefixes(String namespaceURI) {
            return null;
        }
    });

EDIT: made a test run with my fix on your code, is it what you were expecting to see?

found node -> Header (namespace: http://schemas.xmlsoap.org/soap/envelope/)
Wisthler
  • 577
  • 3
  • 13
0

In addition to @Wisthler's answer, this was the only line of code that was missing for making everything work

        factory.setNamespaceAware(true);

So the whole working code is

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new java.io.ByteArrayInputStream(payload.getBytes()));
        XPath xpath = XPathFactory.newInstance().newXPath();
        xpath.setNamespaceContext(new NamespaceContext() {
            @Override
            public String getNamespaceURI(String prefix) {
                if("soapenv".equals(prefix)){
                    return "http://schemas.xmlsoap.org/soap/envelope/";
                }else{
                    return null;
                }
            }

            @Override
            public String getPrefix(String namespaceURI) {
                return null;
            }

            @Override
            public Iterator getPrefixes(String namespaceURI) {
                return null;
            }
        });
        XPathExpression expr = xpath.compile("//soapenv:Envelope//soapenv:Header//text()[normalize-space()]");
        Object result = expr.evaluate(doc, XPathConstants.NODESET);
        NodeList nodes = (NodeList) result;

Thank you @Wisthler!

Gianmarco F.
  • 780
  • 2
  • 12
  • 36