How to build SOAP client in Spring? - java

I am able to send requests to the web service using javax.xml.soap.*, I would like to covert the code to use webServiceTemplate.
I am struggling with creating request and result objects. (sample Ive found is related to xml not SOAP)
I am also wondering if there is any advantages of using
webServiceTemplate over java.xml.soap. If there is not am I doing it correctly? Given that I need to get connected to 20 web services.
The only service it has is findEvents as follows:
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://ticketmaster.productserve.com/v2/soap.php" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<soapenv:Header/>
<soapenv:Body>
<soap:findEvents soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<request xsi:type="soap:Request">
<!--You may enter the following 7 items in any order-->
<apiKey xsi:type="xsd:string">?</apiKey>
<country xsi:type="xsd:string">?</country>
<resultsPerPage xsi:type="xsd:int">?</resultsPerPage>
<currentPage xsi:type="xsd:int">?</currentPage>
<sort xsi:type="soap:Request_Sort">
<!--You may enter the following 2 items in any order-->
<field xsi:type="xsd:string">?</field>
<order xsi:type="xsd:string">?</order>
</sort>
<filters xsi:type="soap:ArrayOfRequest_Filter" soapenc:arrayType="soap:Request_Filter[]"/>
<updatedSince xsi:type="xsd:string">?</updatedSince>
</request>
</soap:findEvents>
</soapenv:Body>
</soapenv:Envelope>
My code is as follows:
try {
SOAPConnectionFactory soapConnectionFactory =
SOAPConnectionFactory.newInstance();
SOAPConnection connection =
soapConnectionFactory.createConnection();
MessageFactory factory =
MessageFactory.newInstance();
SOAPMessage message = factory.createMessage();
SOAPHeader header = message.getSOAPHeader();
header.detachNode();
SOAPBody body = message.getSOAPBody();
SOAPFactory soapFactory =
SOAPFactory.newInstance();
Name bodyName;
bodyName = soapFactory.createName("findEvents",
"xsd", "http://ticketmaster.productserve.com/v2/soap.php");
SOAPBodyElement getList =
body.addBodyElement(bodyName);
Name childName = soapFactory.createName("findEvents");
SOAPElement eventRequest = getList.addChildElement(childName);
childName = soapFactory.createName("apiKey");
SOAPElement apiKey = eventRequest.addChildElement(childName);
apiKey.addTextNode("MYAPI");
childName = soapFactory.createName("country");
SOAPElement cid = eventRequest.addChildElement(childName);
cid.addTextNode("UK");
message.writeTo(System.out); //show message details
URL endpoint = new URL("http://ticketmaster.productserve.com/v2/soap.php");
SOAPMessage response =
connection.call(message, endpoint);
connection.close();
//SOAPBody soapBody = response.getSOAPBody();
SOAPMessage sm = response;
System.out.println("Response:");
ByteArrayOutputStream out = new ByteArrayOutputStream();
sm.writeTo(out);
String validSoap = "<?xml version=\"1.0\"?> " + out.toString();
System.out.println("It is ValidSoap: " + validSoap); //ValidSoap message
SAXBuilder builder = new SAXBuilder();
Reader in = new StringReader(validSoap); //reading character stream
Document doc = null; //empty jDom document is instantiated
doc = builder.build(in); //build the jDom document
Element root = doc.getRootElement(); //Envelope
List allChildren = root.getChildren(); //list of all its child elements
System.out.println("Root is:" + ((Element) allChildren.get(0)).getName());
listChildren(root);
} catch (Exception ex) {
ex.printStackTrace();
}
New Code
webServiceTemplate.sendSourceAndReceiveToResult
("http://ticketmaster.productserve.com/v2/soap.php",source, result);
#XmlRootElement
public class FindEvents {
#XmlElement
Request request;
public Request getRequest() {
return request;
}
public void setRequest(Request request) {
this.request = request;
}
}
#XmlSeeAlso(SortTicket.class)
public class Request {
#XmlElement
String apiKey;
#XmlElement
String country;
#XmlElement
int resultsPerPage;
#XmlElement
int currentPage;
#XmlElement(name = "Sort")
SortTicket sort;
#XmlElement
String[] filters;
#XmlElement
String updatedSince;
public String getApiKey() {
return apiKey;
}
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public int getResultsPerPage() {
return resultsPerPage;
}
public void setResultsPerPage(int resultsPerPage) {
this.resultsPerPage = resultsPerPage;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public SortTicket getSort() {
return sort;
}
public void setSort(SortTicket sort) {
this.sort = sort;
}
public String[] getFilters() {
return filters;
}
public void setFilters(String[] filters) {
this.filters = filters;
}
public String getUpdatedSince() {
return updatedSince;
}
public void setUpdatedSince(String updatedSince) {
this.updatedSince = updatedSince;
}
}
public class SortTicket {
#XmlElement
String field;
#XmlElement
String order;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public String getOrder() {
return order;
}
public void setOrder(String order) {
this.order = order;
}
}

Since you have generated DTO classes with Jaxb annotation you can create a marshaller ,unmarshaller and create objects of the DTO classes (SortTicket,Request,FindEvents) and send the objects directly instead of using the xml request
webServiceTemplate.marshalSendAndReceive(findEvents);
Something like this you'll have to configure.
Create a marshaller
<oxm:jaxb2-marshaller id="marshaller" contextPath="com.yourcontextpath" />
create web service template
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="marshaller" />
<property name="defaultUri"
value="http://ticketmaster.productserve.com/v2/soap.php" />
</bean>
and in some class's method where you want to send soap request inject webServiceTemplate using #Autowired
#Autowired
private WebServiceTemplate webServiceTemplate;
public void sendSampleSoapRequest() {
SortTicket sortTicket=new SortTicket();
// set its values
Request request=new Request();
//set its values
request.setSort(sortTicket);
FindEvents findEvents=new FindEvents();
setRequest(request)
Object response=webServiceTemplate.marshalSendAndReceive(findEvents);
}
marshalSendAndReceive message uses the Jaxb marshaller to convert your objects (marked with JaxB annotation)to xml.So above your findEvents object will be converted to its xml from.
Regarding your second point
Advantages of using webServiceTemplate over java.xml.soap. : you don't have to create those SOAPElements manually you just create an object and send it instead of big code for manually handling it.
Since you'll have to connect to 20 different web services it will be much easier for you to create DTO objects and send them directly.You may need to modify my above samples a little.May remove the deault uri
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="marshaller" />
</bean>
and while sending request give the URI request
Object response=webServiceTemplate.marshalSendAndReceive(uri,object);
For sending it to multiple server
Object response1=webServiceTemplate.marshalSendAndReceive(uri1,object);
Object response1=webServiceTemplate.marshalSendAndReceive(uri2,object)
uri1 and uri2 can be different soap service And if you don't have the wsdl you can send xml with this method
sendSourceAndReceiveToResult(uri1,source, result);
sendSourceAndReceiveToResult(uri2,source, result);
Sending a uri in the send method over rides the default URI
For example check this also check the api doc

Related

Remove empty ns attribute from SOAP request using EndpointInterceptor

There is this legacy system which is sending SOAP requests with an empty namespace attribute causing fields values to be null, when namespace attribute is removed while testing from SOAP UI, it work fine.
This is what problematic SOAP request looks like
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ADD xmlns="http://www.cc.com/ws">
<ID xmlns="">dummy</ID>
<PWD xmlns="">password</PWD>
</ADD>
</soapenv:Body>
</soapenv:Envelope>
I tried by adding an interceptor using addInterceptors method of WsConfigurerAdapter, code of which is pasted below, but it doesn't work
#Override
public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
SOAPMessage soapMessage = ((SaajSoapMessage) messageContext.getRequest()).getSaajMessage();
SOAPBody body = soapMessage.getSOAPBody();
Iterator<SOAPBodyElement> it = body.getChildElements();
while (it.hasNext()) {
Object node = it.next();
if (!isTextNode(node)) {
com.sun.xml.internal.messaging.saaj.soap.ver1_1.BodyElement1_1Impl e =
(com.sun.xml.internal.messaging.saaj.soap.ver1_1.BodyElement1_1Impl) node;
Iterator<SOAPBodyElement> subItr = e.getChildElements();
while(subItr.hasNext()) {
Object subNode = subItr.next();
if(subNode instanceof com.sun.xml.internal.messaging.saaj.soap.impl.ElementImpl) {
SOAPElement el = (SOAPElement) subNode;
el.removeAttribute("xmlns");
}
}
}
}
soapMessage.writeTo(System.out);
return true;
}
Note that a namespace declaration in XML is not an attribute. Don't be confused by the similar syntax!
So I would not expect e.removeAttribute to do anything, because there is no attribute with the name xmlns.
What you actually need to do is to change the name of the element, e.g. using its setElementQName() method.
#Override
public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
SOAPMessage soapMessage = ((SaajSoapMessage) messageContext.getRequest()).getSaajMessage();
SOAPBody body = soapMessage.getSOAPBody();
Iterator<SOAPBodyElement> it = body.getChildElements();
while (it.hasNext()) {
Object node = it.next();
if (!isTextNode(node)) {
com.sun.xml.internal.messaging.saaj.soap.ver1_1.BodyElement1_1Impl e =
(com.sun.xml.internal.messaging.saaj.soap.ver1_1.BodyElement1_1Impl) node;
Iterator<SOAPBodyElement> subItr = e.getChildElements();
while(subItr.hasNext()) {
Object subNode = subItr.next();
if(subNode instanceof com.sun.xml.internal.messaging.saaj.soap.impl.ElementImpl) {
SOAPElement el = (SOAPElement) subNode;
el.setElementQName(
new QName(
e.getElementName().getURI(),
el.getElementName().getLocalName()
)
);
}
}
}
}
soapMessage.writeTo(System.out);
return true;
}

Reverse engineering: How to generate SOAP Request XML in the backend?

I have the following classes:
WS Interface:
package com.mypackage;
import javax.ejb.Remote;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
#Remote
#SOAPBinding(style = Style.DOCUMENT)
#WebService(name = "MathService", targetNamespace = "http://mypackage.com/")
public interface MathServiceWS {
#WebResult(name = "result", targetNamespace = "http://mypackage.com/")
#RequestWrapper(localName = "addRequest", className = "AddRequest", targetNamespace = "http://mypackage.com/")
#WebMethod(action = "http://mypackage.com/add", operationName = "add")
#ResponseWrapper(localName = "addResponse", className = "AddResponse", targetNamespace = "http://mypackage.com/")
Long add(#WebParam(name = "add", targetNamespace = "http://mypackage.com/") AddBean add);
}
WS Implementation:
package com.mypackage;
import javax.ejb.Stateless;
import javax.jws.WebService;
#Stateless(mappedName = "MathService")
#WebService(serviceName = "MathService", endpointInterface = "com.mypackage.MathServiceWS", portName = "MathServicePort", targetNamespace = "http://mypackage.com/")
public class MathService implements MathServiceWS {
#Override
public Long add(AddBean add) {
Long first = new Long(add.getFirst().intValue());
Long second = new Long(add.getSecond().intValue());
return Long.valueOf(Math.addExact(first.longValue(), second.longValue()));
}
}
The bean:
package com.mypackage;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(
name = "Add",
namespace = "http://mypackage.com/",
propOrder = {
"first",
"second"
}
)
public class AddBean implements Serializable {
private static final long serialVersionUID = -7727938355039425419L;
#XmlElement(required = true)
private Integer first;
#XmlElement(required = true)
private Integer second;
public AddBean() {
}
public Integer getFirst() {
return first;
}
public void setFirst(Integer first) {
this.first = first;
}
public Integer getSecond() {
return second;
}
public void setSecond(Integer second) {
this.second = second;
}
}
After deploying this WS, when I'm adding the WSDL in SoapUI, the add method request is as follows after giving the user input:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:myp="http://mypackage.com/">
<soapenv:Header/>
<soapenv:Body>
<myp:addRequest>
<myp:add>
<first>1</first>
<second>2</second>
</myp:add>
</myp:addRequest>
</soapenv:Body>
</soapenv:Envelope>
Now I want to have the above SOAP request XML in my com.mypackage.MathService.add(AddBean) method with the given user input.
Using JAXB on com.mypackage.AddBean only generates partial request
The WebService Handlers is not useful to fulfill my requirement
Any pointer would be very helpful.
You may create an custom SOAPHandler object and can read the request payload and set it to SOAPMessageContext via custom property. Make sure you set the scope as application.
In your service class, inject the javax.xml.ws.WebServiceContext using #javax.annotation.Resource and access payload set via your custom property.
For example:
1. Create Handler and register it.
public class PopulateSOAPMessagePayloadHandler implements SOAPHandler<SOAPMessageContext> {
public static final String SOAP_MESSAGE_PAYLOAD = "__soap_message_payload";
#Override
public boolean handleMessage(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (!outboundProperty.booleanValue()) {
// for incoming:
ByteArrayOutputStream bout = new ByteArrayOutputStream(1024);
try {
smc.getMessage().writeTo(bout);
String payload = bout.toString(StandardCharsets.UTF_8.name());
smc.put(SOAP_MESSAGE_PAYLOAD, payload); //Set payload
smc.setScope(SOAP_MESSAGE_PAYLOAD, MessageContext.Scope.APPLICATION); //make it application scope
} catch (SOAPException | IOException e) {
e.printStackTrace();
// handle exception if needed
throw new WebServiceException(e);
}
}
return true;
}
// Other method (no-op) omitted
}
2. Get the payload
public class MathService implements MathServiceWS {
#Resource
private WebServiceContext context;
#Override
public Long add(AddBean add) {
String payload = (String) context.getMessageContext().get(SOAP_MESSAGE_PAYLOAD);
Long first = new Long(add.getFirst().intValue());
Long second = new Long(add.getSecond().intValue());
return Long.valueOf(Math.addExact(first.longValue(), second.longValue()));
}
}
Hope it helps.
You can get full control of the document easily. First lets setup the bean:
#XmlRootElement(name="addRequest")
#XmlAccessorType(XmlAccessType.FIELD) //Probably you don't need this line. it is by default field accessible.
public class AddBean implements Serializable {
private static final long serialVersionUID = -7727938355039425419L;
#XmlElement(name="first",required = true) //you don't need name attribute as field is already exactly the same as soap element
private Integer first;
#XmlElement(name="second",required = true) //you don't need name attribute as field is already exactly the same as soap element
private Integer second;
public AddBean() { }
//Getters and Setters
}
Now, I think this is the part you are looking for. To add custom namespace declarations and set prefix etc. If you are using org.springframework.ws.client.core.support.WebServiceGatewaySupport.getWebServiceTemplate to make SOAP request, then you can do the following:
public class WSCastorClient extends WebServiceGatewaySupport {
public CustomResponseObject callWebService(Addbean add) {
WebServiceTemplate wst = getWebServiceTemplate();
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.afterPropertiesSet();
wst.setMarshaller(marshaller);
wst.setUnmarshaller(marshaller);
wst.afterPropertiesSet();
CustomResponseObject response = (CustomResponseObject)
wst.marshallSendAndReceive(add, new
WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) {
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) message;
SOAPMesage soapMEssage = saajSoapMessage.getSaajMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
SOAPHeader head = soapMessage.getSOAPHeader();
SOAPBody soapBody = soapMessage.getSOAPBody();
//Now you have full control of the soap header, body, envelope. You can add any namespace declaration, prefix, add header element, etc. You can add remove whatever you want.
soapEnvelope.removeNamespaceDeclaration(soapEnvelope.getPrefix()); //clear whatever namespace is there
soapEnvelope.addNamespaceDeclaration("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
soapEnvelope.addNamespaceDeclaration("myp", "http://mypackage.com/");
soapEnvelope.setPrefix("soapenv");
soapHeader.setPrefix("soapenv");
soapBody.setPrefix("soapenv");
Document doc = saajSoapMessage.getDocument();
StringWriter sw = new StringWriter();
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(new DOMSource(doc), new StreamResult(sw));
}
});
return response;
}
//close off other brackets if I forgot any.

Getting content of a SOAP Header using Spring WS

I'm trying to build an endpoint that will receive SOAP messages from a client. The message I'm receiving contains a username and password inside the soap header ...
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://www.company.com/Application">
<soapenv:Header xmlns:wsse="http://__________.xsd">
<wsse:Security >
<wsse:UsernameToken>
<wsse:Username>username</wsse:Username>
<wsse:Password>password</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
I'm using Spring WS - the obvious solution is to create a filter inside web.xml that will bypass Spring WS completely, parse the SOAP message, extract the username and password and then continue to Spring WS which will parse the SOAP again.
Is there a way to get the content of the header without circumventing Spring WS?
I've tried adding a bean inside sws:interceptors:
<sws:interceptors>
<!-- extract Security details from Header -->
<bean class="com.company.application.service.SecurityInterceptorService" />
<!-- log full Body of request -->
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
<!-- validate Request against XSD to make sure it's a valid request -->
<bean id="CompanyApplication" class="com.company.application.interceptor.ValidatingInterceptor">
<property name="schema" value="/WEB-INF/_______________.xsd" />
<property name="validateRequest" value="true" />
<property name="validateResponse" value="true" />
</bean>
</sws:interceptors>
and then implementing that class:
public class SecurityInterceptorService implements EndpointInterceptor {
#Override
public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
System.out.println("---------------");
System.out.println("handleRequest") ;
System.out.println("---------------");
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
System.out.println("---------------");
System.out.println("handleResponse");
System.out.println("---------------");
return true;
}
#Override
public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {
System.out.println("---------------");
System.out.println("handleFault");
System.out.println("---------------");
return true;
}
#Override
public void afterCompletion(MessageContext messageContext, Object endpoint, Exception ex) throws Exception {
System.out.println("---------------");
System.out.println("afterCompletion");
System.out.println("---------------");
}
}
endpoint only contains data about the endpoint inside handleRequest and after traversing through many layers and layers inside messageContext while in debug mode, I can't seem to spot the content of the header.
Is the content I'm looking for inside messageContext and if so, how do I access it?
From the messageContext object, you can retrieve either the request or the response (In your case, I guess you need the request).
The request/response is basically a WebServiceMessage. If you examine the webServiceMessage, you will see that the object can be casted to a SoapMessage. From the soap message, you can now get the soap header.
WebServiceMessage webServiceMessageRequest = messageContext_.getRequest();
SoapMessage soapMessage = (SoapMessage) webServiceMessageRequest;
SoapHeader soapHeader = soapMessage.getSoapHeader()
Afterwards, You might want to get the source object and convert it to a DOMSource object and then get the Node object which make the information retrieval much easier.
Source bodySource = soapHeader .getSource();
DOMSource bodyDomSource = (DOMSource) bodySource;
Node bodyNode = _bodyDomSource.getNode();
If you are using spring-boot you can use this kind of configuration:
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
PayloadValidatingInterceptor validatingInterceptor = new PayloadValidatingInterceptor();
validatingInterceptor.setValidateRequest(true);
validatingInterceptor.setValidateResponse(true);
validatingInterceptor.setXsdSchema(resourceSchema());
interceptors.add(validatingInterceptor);
}
#Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/api/*");
}
#Bean(name = "registros")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("ResourcePort");
wsdl11Definition.setLocationUri("/api");
wsdl11Definition.setTargetNamespace("http://resource.com/schema");
wsdl11Definition.setSchema(resourceSchema());
return wsdl11Definition;
}
#Bean
public XsdSchema resourceSchema() {
return new SimpleXsdSchema(new ClassPathResource("registro.xsd"));
}
}
In this example the addInterceptors method is the important one, the others 3 are basic to expose a WSDL API.
Maybe it'll be useful for someone else.
There is no easy way to unmarshall Soap headers with Spring-ws (it's currently not supported)
However, you can access the SoapHeaderElement in your #PayloadRoot annotated method, and do the process of unmarshalling with JAXB.
#Endpoint
public class SubmitEndpoint implements EndpointInterface {
private static final String NAMESPACE_URI = "http://www.example.com/namespace";
private Security unmarshallSecurityFromSoapHeader(SoapHeaderElement header) {
Security security = null;
try {
JAXBContext context = JAXBContext.newInstance(Security.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
security = (Security) unmarshaller.unmarshal(header.getSource());
} catch (JAXBException e) {
e.printStackTrace();
}
return security;
}
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "submit")
#ResponsePayload
public SubmitResponse submit(#RequestPayload Submit submit, #SoapHeader(
value = "{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security") SoapHeaderElement wsseSecurityHeader) throws JAXBException {
Security security = unmarshallSecurityFromSoapHeader(wsseSecurityHeader);
}
}
Security.java
#Getter
#Setter
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(namespace = Security.SECURITY_NS, name = "Security")
public class Security {
public static final String SECURITY_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
#XmlElement(namespace = SECURITY_NS, name = "UsernameToken")
private UsernameToken usernameToken;
}
UsernameToken.java
#Getter
#Setter
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(namespace = Security.SECURITY_NS, name = "UsernameToken")
public class UsernameToken {
#XmlElement(name = "Username", namespace = Security.SECURITY_NS)
private String username;
#XmlElement(name = "Password", namespace = Security.SECURITY_NS)
private String password;
}

How to add soap header when making a soap request using the java objects generated by wsdl

I generated client java objects using JAX-WS RI. I am trying to make a SOAP request to a web service. Service requires authentication in the header which looks like below:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<xsd:authHeader>
<xsd:user>username#gmail.com</xsd:user>
<xsd:password>password1</xsd:password>
</xsd:authHeader>
</soapenv:Header>
<soapenv:Body>
<ns:searchAssetsParam>
<ns:includeSubfolders>true</ns:includeSubfolders>
<ns:resultsPage>2</ns:resultsPage>
</ns:searchAssetsParam>
</soapenv:Body>
</soapenv:Envelope>
The generated java objects have methods for calling the service, creating the objects and constructing the header. But, I am having trouble setting the header while making the call.
Here's the code that I am using:
IpsApiService service = new IpsApiService();
IpsApiPortType port = service.getIpsApiSoapPort();
SearchAssetsParam searchAssetsParam = buildSearchAssetsParam();
SearchAssetsReturn response = port.searchAssets(searchAssetsParam);
buildSearchAssetsParam() constructs the request object.
I created the header object as follows:
AuthHeader header = new AuthHeader();
header.setUser("username#gmail.com");
header.setPassword("password1");
How do I set this AuthHeader to the service request?
Thanks,
Venu
Once I had the same problem. I needed to modify the JAX-WS web service SOAP header at every request. To solve this problem I have created a handler like this:
public class MyHandler implements SOAPHandler<SOAPMessageContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(MyHandler.class);
private String username;
private String password;
#Override
public boolean handleMessage(SOAPMessageContext context) {
try {
SOAPMessage message = context.getMessage();
SOAPHeader header = message.getSOAPHeader();
SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
if (header == null) {
header = envelope.addHeader();
}
QName qNameUserCredentials = new QName("https://your.target.namespace/", "UserCredentials");
SOAPHeaderElement userCredentials = header.addHeaderElement(qNameUserCredentials);
QName qNameUsername = new QName("https://your.target.namespace/", "Username");
SOAPHeaderElement username = header.addHeaderElement(qNameUsername );
username.addTextNode(this.username);
QName qNamePassword = new QName("https://your.target.namespace/", "Password");
SOAPHeaderElement password = header.addHeaderElement(qNamePassword);
password.addTextNode(this.password);
userCredentials.addChildElement(username);
userCredentials.addChildElement(password);
message.saveChanges();
//TODO: remove this writer when the testing is finished
StringWriter writer = new StringWriter();
message.writeTo(new StringOutputStream(writer));
LOGGER.debug("SOAP message: \n" + writer.toString());
} catch (SOAPException e) {
LOGGER.error("Error occurred while adding credentials to SOAP header.", e);
} catch (IOException e) {
LOGGER.error("Error occurred while writing message to output stream.", e);
}
return true;
}
//TODO: remove this class after testing is finished
private static class StringOutputStream extends OutputStream {
private StringWriter writer;
public StringOutputStream(StringWriter writer) {
this.writer = writer;
}
#Override
public void write(int b) throws IOException {
writer.write(b);
}
}
#Override
public boolean handleFault(SOAPMessageContext context) {
LOGGER.debug("handleFault has been invoked.");
return true;
}
#Override
public void close(MessageContext context) {
LOGGER.debug("close has been invoked.");
}
#Override
public Set<QName> getHeaders() {
LOGGER.debug("getHeaders has been invoked.");
return null;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
}
It adds the needed parameters to my SOAP header and it is invoked on every request. All you need to do is to modify handleMessage method to suit your needs.
It works for me by overriding the public void setAttribute(String namespace, String localName, String value) method.
import javax.xml.namespace.QName;
import org.apache.axis.Constants;
import org.apache.axis.message.SOAPHeaderElement;
#SuppressWarnings("serial")
public class ADESHeaderElement extends SOAPHeaderElement
{
public ADESHeaderElement(QName qname, Object value)
{
super(qname, value);
}
#Override
public void setAttribute(String namespace, String localName, String value)
{
if (!Constants.ATTR_MUST_UNDERSTAND.equals(localName))
{ // Or any other attribute name you'd want to avoid
super.setAttribute(namespace, localName, value);
}
}
}
Create header element like this:
ADESHeaderElement custheader = new ADESHeaderElement(qname, clientserv);
custheader.setActor(null);
When you create your service from classess generated by cxf, add custom interceptor
Service service = new MyService(wsdlURL, new QName("http://myservice.com/MyService/", "MyService"));
MyPort port = service.getMyPort();
Client client = ClientProxy.getClient(port);
// adding interceptor programmatically
client.getOutInterceptors().add(new MyHeaderHandler());
Your can extend AbstractSoapInterceptor to implement your custom interceptor to handle message.
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.headers.Header;
import org.apache.cxf.jaxb.JAXBDataBinding;
import org.apache.cxf.phase.Phase;
import com.rpc.core.utils.DomainContext;
public class MyHeaderHandler extends AbstractSoapInterceptor {
/**
* Constructor
*/
public MyHeaderHandler() {
super(Phase.PRE_LOGICAL);
}
#Override
public void handleMessage(org.apache.cxf.binding.soap.SoapMessage message) throws org.apache.cxf.interceptor.Fault {
try {
message.getHeaders().add(new Header(new QName("MyCustomHeader"),"value", new JAXBDataBinding(String.class)));
} catch (JAXBException e) {
e.printStackTrace();
}
};
}
}
Yes, I did the same that Rangappa Tungal, following this example:
Service w = new ServiceLocator();
ServiceSoap ws = new ServiceSoapStub(new URL(w.getServiceSoapAddress()),w); Stub mystub = (Stub) ws;
AuthHeader up = new AuthHeader("user","pass");
mystub.setHeader("namespace", "AuthHeader", up);
ws.get***();
Link to the example!

How to get SOAP headers

Here is the request
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soap="http://soap.ws.server.wst.fit.cvut.cz/">
<soapenv:Header>
<userId>someId</userId>
</soapenv:Header>
<soapenv:Body>
...
</soapenv:Body>
</soapenv:Envelope>
and I want to get that userId.
I tried this
private List<Header> getHeaders() {
MessageContext messageContext = context.getMessageContext();
if (messageContext == null || !(messageContext instanceof WrappedMessageContext)) {
return null;
}
Message message = ((WrappedMessageContext) messageContext).getWrappedMessage();
return CastUtils.cast((List<?>) message.get(Header.HEADER_LIST));
}
private String getHeader(String name) {
List<Header> headers = getHeaders();
if (headers != null) {
for (Header header : headers) {
logger.debug(header.getObject());
// return header by the given name
}
}
return null;
}
And it logs [userId : null]. How can I get the value and why is null there?
"[userId : null]" is generally the "toString" printout of a DOM element. Most likely if you do something like
logger.debug(header.getObject().getClass())
you will see that it is a DOM Element subclass of somesort. Thus, something like:
logger.debug(((Element)header.getObject()).getTextContent())
might print the text node.
import javax.xml.soap.*;
SOAPPart part = request.getSOAPPart();
SOAPEnvelope env = part.getEnvelope();
SOAPHeader header = env.getHeader();
if (header == null) {
// Throw an exception
}
NodeList userIdNode = header.getElementsByTagNameNS("*", "userId");
String userId = userIdNode.item(0).getChildNodes().item(0).getNodeValue();
You can get soap headers without Interceptors and without JAXB.
In your service_impl class add :
public class YourFunctionNameImpl implements YourFunctionName{
#Resource
private WebServiceContext context;
private List<Header> getHeaders() {
MessageContext messageContext = context.getMessageContext();
if (messageContext == null || !(messageContext instanceof WrappedMessageContext)) {
return null;
}
Message message = ((WrappedMessageContext) messageContext).getWrappedMessage();
List<Header> headers = CastUtils.cast((List<?>) message.get(Header.HEADER_LIST));
return headers;
}
...
Then in your function you can use:
List<Header> headers = getHeaders();
for(Iterator<Header> i = headers.iterator(); i.hasNext();) {
Header h = i.next();
Element n = (Element)h.getObject();
System.out.println("header name="+n.getLocalName());
System.out.println("header content="+n.getTextContent());
}
We can get SOAP header in server side by adding following code in CXF interceptor.
Create a class like
public class ServerCustomHeaderInterceptor extends AbstractSoapInterceptor {
#Resource
private WebServiceContext context;
public ServerCustomHeaderInterceptor() {
super(Phase.INVOKE);
}
#Override
public void handleMessage(SoapMessage message) throws Fault,JAXBException {
System.out.println("ServerCustomHeaderInterceptor handleMessage");
JAXBContext jc=null;
Unmarshaller unmarshaller=null;
try {
jc = JAXBContext.newInstance("org.example.hello_ws");
unmarshaller = jc.createUnmarshaller();
} catch (JAXBException e) {
e.printStackTrace();
}
List<Header> list = message.getHeaders();
for (Header header : list) {
ElementNSImpl el = (ElementNSImpl) header.getObject();
ParentNode pn= (ParentNode) el.getFirstChild();
//Node n1= (Node) pn;
//Node n1= (Node) el.getFirstChild();
CustomHeader customHeader=(CustomHeader) unmarshaller.unmarshal(el.getFirstChild());
}
}
After this we need to inject this as a interceptor like
<jaxws:inInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
<bean class="org.example.hellows.soap12.ServerCustomHeaderInterceptor" />
</jaxws:inInterceptors>
in your server context xml.
We may need to change few lines as per your requirements. Basic flow will work like this.
Having a MessageContext messageContext, you can use this code:
HeaderList hl = (HeaderList) messageContext.get(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY);
which gives you access to all SOAP headers.

Categories