How can I add SOAP Headers to Spring Jax-WS Client?
Specifically, I have a Jaxb object I would like to add to the header but xml examples would be appreciated.
I am using Spring's JaxWsPortProxyFactoryBean described here. Also, I am generating my client as described here which is working less the headers I need to add.
Thank you.
A little bit more elegant (still a class cast is required):
public void doWithMessage(WebServiceMessage message) {
try {
SOAPMessage soapMessage = ((SaajSoapMessage)message).getSaajMessage();
SOAPHeader header = soapMessage.getSOAPHeader();
SOAPHeaderElement security = header.addHeaderElement(new QName("http://schemas.xmlsoap.org/ws/2003/06/secext", "Security", "wsse"));
SOAPElement usernameToken = security.addChildElement("UsernameToken", "wsse");
SOAPElement username = usernameToken.addChildElement("Username", "wsse");
SOAPElement password = usernameToken.addChildElement("Password", "wsse");
username.setTextContent(someUsername);
password.setTextContent(somePassword);
} catch (Exception e) {
//... handle appropriately
}
}
Note: This example has been testes with Spring WS 2.1.4.
I'm still trying to find an elegant way to add headers, but what I do as suggested by others is to use a Transformer on the WebServiceMessageCallBack(). Here's an example code:
JAXBElement<GetDeletedResponse> result = (JAXBElement<GetDeletedResponse>) webServiceTemplate.marshalSendAndReceive(request, new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage webServiceMessage) {
try {
SoapMessage soapMessage = (SoapMessage) webServiceMessage;
soapMessage.setSoapAction("getDeleted");
SoapHeader header = soapMessage.getSoapHeader();
StringSource headerSource = new StringSource("<account>\n" +
"<username>"+"johnsmith"+"</username>\n" +
"<password>"+"1234"+"</password>\n" +
"</account>");
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(headerSource, header.getResult());
} catch (Exception e) {
new RuntimeException(e);
}
}
...
It's not really elegant, considering this is Spring's WS. It's not intuitive.
After some poking around if found a slightly different solution. I'm using JAXB for marshalling my payload and the possible header classes have also been generated with JAXB from the WSDL.
In my case I am addressing Microsoft Reporting Services and have pass on an ExecutionID as SOAP header.
public class ReportExecution2005Client extends WebServiceGatewaySupport {
private static final String SET_EXECUTION_PARAMETERS_ACTION = "http://schemas.microsoft.com/sqlserver/2005/06/30/reporting/reportingservices/SetExecutionParameters";
private final class SoapActionExecutionIdCallback implements WebServiceMessageCallback {
private final String soapAction;
private final String executionId;
public SoapActionExecutionIdCallback(String soapAction, String executionId) {
super();
this.soapAction = soapAction;
this.executionId = executionId;
}
@Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
SoapMessage soapMessage = (SoapMessage) message;
soapMessage.setSoapAction(soapAction);
ExecutionHeader executionHeader = new ExecutionHeader();
executionHeader.setExecutionID(executionId);
getMarshaller().marshal(executionHeader, soapMessage.getSoapHeader().getResult());
}
}
public void setExecutionParameters(String executionId){
SetExecutionParameters request = new SetExecutionParameters();
request.setParameters(new ArrayOfParameterValue());
SetExecutionParametersResponse response = (SetExecutionParametersResponse) getWebServiceTemplate().marshalSendAndReceive(request,
new SoapActionExecutionIdCallback(
SET_EXECUTION_PARAMETERS_ACTION,
executionId));
}
}
Basically the WebServiceGatewaySupport already knows the Marshaller to convert JAXB Pojos. I'm using this one to attach my own header classes to the SoapHeader with this line:
getMarshaller().marshal(executionHeader, soapMessage.getSoapHeader().getResult());
in my nested WebServiceMessageCallback.
This is an alternative solution:
public class YourServiceClient extends WebServiceGatewaySupport {
// custom header inner class
private final class CustomHeader implements WebServiceMessageCallback {
private String soapAction;
private long yourHeaderParameter1;
private long yourHeaderParameter2;
public CustomHeader(String soapAction, long yourHeaderParameter1, long yourHeaderParameter2) {
super();
if (!StringUtils.hasText(soapAction)) {
soapAction = "\"\"";
}
this.soapAction = soapAction;
this.yourHeaderParameter1 = yourHeaderParameter1;
this.yourHeaderParameter2 = yourHeaderParameter2;
}
@Override
public void doWithMessage(WebServiceMessage message) {
try {
// get the header from the SOAP message
SoapHeader soapHeader = ((SoapMessage) message).getSoapHeader();
// create the header element
ObjectFactory factory = new ObjectFactory();
YourGeneratedHeaderEntity header = new YourGeneratedHeaderEntity();
header.setyourHeaderParameter1(yourHeaderParameter1);
header.setyourHeaderParameter2(yourHeaderParameter2);
JAXBElement<YourGeneratedHeaderEntity> headers = factory.createYourGeneratedHeaderEntity(header);
// create a marshaller
JAXBContext context = JAXBContext.newInstance(YourGeneratedHeaderEntity.class);
Marshaller marshaller = context.createMarshaller();
// set action
Assert.isInstanceOf(SoapMessage.class, message);
SoapMessage soapMessage = (SoapMessage) message;
soapMessage.setSoapAction(soapAction);
// marshal the headers into the specified result
marshaller.marshal(headers, soapHeader.getResult());
} catch (Exception e) {
logger.error(e.getLocalizedMessage());
}
}
}
public YourEntityResponse getYourService(long yourHeaderParameter1, long yourHeaderParameter2) {
GetYourService request = new GetYourService();
YourEntityResponse response = (YourEntityResponse) getWebServiceTemplate()
.marshalSendAndReceive(request, new CustomHeader("https://your.service.asmx?WSDL", yourHeaderParameter1, yourHeaderParameter2));
return response;
}
}