Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add custom element to SOAP header in Spring WS

I'm coding a web services client application using Spring WS with JAXB binding. The services I'm using requiers authentication via wsse:security element in SOAP header (and one more custom header). I have all the necessary schemas compiled, including wsse.xsd compiled to org.xmlsoap.schemas.ws._2002._12.secext.Security.

Yet I can't find a way to insert this or any other element into SOAP header. I know I can use interceptors or SoapActionCallbacks, but what they allow me to do is to manually construct the header and add it to header section via ((SaajSoapMessage)webServiceMessage).getSoapHeader().addHeaderElement(qName) and so forth. But I don't want to build this header manually, as I have a corresponding class that I can easily marshall.

My question - is there a way to insert an object into SOAP header (or other part of the envelope) when invoking web service call in Spring WS? I used Apache CXF and Axis2 to consume web services and this was never an issue - frameworks just did it for me behind the scenes (usually via service stubs mechanism).

like image 589
nachteil Avatar asked Sep 13 '25 12:09

nachteil


1 Answers

I've managed to solve this somehow, thanks @GPI for tips. I'm fairly new to the Spring WS and javax.xml.whatever, so I can't tell if this is either the right or elegant way of doing this, but it does exactly what I want.

This code adds custom header elements to <SOAP-ENV:Header>, based on my objects generated from XSD schemas via JAXB. I have no idea how does the Transformer know where I want to put these elements, but it places them correctly in Header section.

public class HeaderComposingCallback implements WebServiceMessageCallback {

    private final String action;

    public HeaderComposingCallback( String action ) {
        this.action = action;
    }

    @Override
    public void doWithMessage(WebServiceMessage webServiceMessage) throws IOException, TransformerException {

    SoapHeader soapHeader = ((SoapMessage)webServiceMessage).getSoapHeader();

    try {
        JAXBContext context = JAXBContext.newInstance( MessageHeader.class, Security.class );

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        Document securityDocument = builder.newDocument();
        Document headerDocument = builder.newDocument();

        Marshaller marshaller = context.createMarshaller();
        marshaller.marshal( HeaderFactory.getHeader( action ), headerDocument );
        marshaller.marshal( SecurityFactory.getSecurity(), securityDocument );

        Transformer t = TransformerFactory.newInstance().newTransformer();

        DOMSource headerSource = new DOMSource( headerDocument );
        DOMSource securitySource = new DOMSource( securityDocument );

        t.transform( headerSource, soapHeader.getResult() );
        t.transform( securitySource, soapHeader.getResult() );

    } catch (JAXBException | ParserConfigurationException e) {
        e.printStackTrace();
    }
}

}

Then I simply pass HeaderComposingCallback object to marshalSendAndReceive() method during service call.

EDIT (after Arjen's comment)

Arjen's right. What I wanted to do could be achieved simplier. Now my doWithMessage method looks like this:

    @Override
    public void doWithMessage(WebServiceMessage webServiceMessage) throws IOException, TransformerException {

    SoapHeader soapHeader = ((SoapMessage)webServiceMessage).getSoapHeader();

    try {
        JAXBContext context = JAXBContext.newInstance( MessageHeader.class, Security.class );

        Marshaller marshaller = context.createMarshaller();
        marshaller.marshal( header, soapHeader.getResult() );
        marshaller.marshal( security, soapHeader.getResult() );

    } catch (JAXBException e) {
        e.printStackTrace();
    }
}
like image 66
nachteil Avatar answered Sep 15 '25 02:09

nachteil