Add custom element to SOAP header in Spring WS

I am coding a web services client application using Spring WS with JAXB binding. Services where I am using requiers authentication via wsse: security element in SOAP header (and another custom header). I have all the schematics I need, including those wsse.xsd

compiled for org.xmlsoap.schemas.ws._2002._12.secext.Security

.

However, I cannot find a way to insert this or any other element into the SOAP header. I know I can use interceptors or SoapActionCallback

s, but what they allow me to do is manually create a header and add it to the header section via ((SaajSoapMessage)webServiceMessage).getSoapHeader().addHeaderElement(qName)

and so on. But I don't want to create this header manually as I have a corresponding class that I can easily customize.

My question is, is there a way to inject an entity into a SOAP header (or other part of an envelope) when invoking a web service in Spring WS? I have used Apache CXF and Axis2 to consume web services and this has never been a problem - frameworks just did it for me behind the scenes (usually via a service stub mechanism).

+3


source to share


1 answer


I managed to solve this somehow, thanks @GPI for the advice. I'm new to Spring WS and javax.xml.whatever, so I can't tell if this is the correct or elegant way to do it, 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 the Transformer knows where I want to put these elements, but it puts them in the header section correctly.

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 just pass the object to the HeaderComposingCallback

method marshalSendAndReceive()

during the service call.



EDIT (after Arjen's comment)

Arjen is on the right. What I wanted to do could have been easier. Now my method doWithMessage

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();
    }
}

      

+3


source







All Articles