Below are my web-service request, Route and Request-Validator,
Web-service request:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<stlh:SabreHeader xmlns:stlh="http://services.sabre.com/STL_Header/v02_01">
<stlh:Service version="1.0.0">GetHotelMediaRQ</stlh:Service>
<stlh:Identification>
<stlh:CustomerID>CID12345</stlh:CustomerID>
<stlh:CustomerAppID>AppTest</stlh:CustomerAppID>
<stlh:ConversationID>05EFPElI2A4KudU75863JIxqAhQJtAx0</stlh:ConversationID>
<stlh:MessageID>4DTTQaHGSifFUtmSoMHAiq</stlh:MessageID>
<stlh:TimeStamp>2014-11-07T14:45:42.725-06:00</stlh:TimeStamp>
</stlh:Identification>
</stlh:SabreHeader>
<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" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" wsu:Id="athId">${athId}</wsse:BinarySecurityToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<GetHotelMediaRQ xmlns="http://services.sabre.com/hotel/media/v1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0.0" xsi:schemaLocation="http://services.sabre.com/hotel/media/v1 GetHotelMediaRQ.xsd">
<HotelRefs>
<HotelRef HotelCode="184769" CodeContext="Sabre">
<ImageRef MaxImages="1">
<Images>
<Image Type="ORI"/>
</Images>
<AdditionalInfo>
<Info Type="CAPTION">true</Info>
</AdditionalInfo>
<Languages>
<Language Code="EN"/>
</Languages>
</ImageRef>
</HotelRef>
</HotelRefs>
</GetHotelMediaRQ>
</soap:Body>
</soap:Envelope>
RequestValidator:
public void validate(GetHotelMediaRQ request, Exchange exchange) throws Exception {
TransactionContext context = BusExtensions.getTransactionContext(exchange);
Collection<HotelRef> hotelRefList = getInstance().convert(request, Collection.class);
Set<Property> properties = new HashSet<>();
String customerAppId = exchange.getIn().getHeader("customerAppID", String.class);
String customerId = exchange.getIn().getHeader("customerID", String.class);
But customerAppId(AppTest) and CustomerId(CI12345) is coming as null when I try to access via Exchange object.
"Custom" Soap headers are not copied to camel header . you have to manually add soap header into camel exchange header .
Approach 1 )
CamelCxfMessage - you can extract/process custom soap header camel cxf message which is present in camel exchange header
in camel -
SoapMessage soapMessage = (SoapMessage)exchange.getIn().getHeader("CamelCxfMessage");
this will give you soap message and its soapMessage.getExchange and try to get soap headers from soap message and process it .
Approach 2)
Camel Cxf Binding -you can use camel cxf binding feature in endpoint definition like cxfBinding=#bindingName .
Create a class and extend with org.apache.camel.component.cxf.DefaultCxfBinding and bean name should be bindingName .
it has one method which you have to overwrite - propagateHeadersFromCxfToCamel(camelmessage ,cxfmessage ,exchage ).
Here get your soap header and put it in camel header with identifier and access header in camel exchange header in processor or routes with same identifier.
Set logging for org.apache.camel to DEBUG and the header values will be logged, and you can determine if the component is dropping them or not.
Also, it looks like you might be using the cxf soap endpoint. Look into the [Description of relayHeaders option] section of the docs here:
http://camel.apache.org/cxf.html
i had to extract header information but got null in the object at the first attempt. then after a while i could fish it out. here is how (in a processor):
#Override
public void process(Exchange exc) throws Exception {
#SuppressWarnings("unchecked")
List<SoapHeader> headers = exc.getIn().getHeader(Header.HEADER_LIST, List.class);
for (int i=0; i < headers.size(); i++) {
if (headers.get(i).getObject() instanceof ElementNSImpl) {
ElementNSImpl elementNSImpl = (ElementNSImpl) headers.get(i).getObject();
Node firstChild = elementNSImpl.getFirstChild();
log.trace("header: name=" + elementNSImpl.getLocalName() + ", value=" + firstChild.getNodeValue());
}
}
Related
I have requirements where I have to add response details in Body tag instead of Header. For now details are added in Header tag of soap response.
#XmlType
public class CheckBalanceResponse {
private String checkBalanceResult;
#XmlElement(name="CheckBalanceResult")
public String getCheckBalanceResult() {
return checkBalanceResult;
}
public void setCheckBalanceResult(String checkBalanceResult) {
this.checkBalanceResult = checkBalanceResult;
}
}
Generated response is below:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<CheckBalanceResponse xmlns="http://charginggw.org/">
<CheckBalanceResult>Incorrect PIN</CheckBalanceResult>
</CheckBalanceResponse>
</soap:Header>
<soap:Body/>
</soap:Envelope>
I want CheckBalanceResponse tag to be in Body instead of Header. Please suggest what should be done?
What's my goal?
I'm rather new to Spring WS, I got a WSDL (and along some XSDs, ofcourse) and i want to add some custom header elements to the SOAP response.
I've been searching the web, tried various code pieces, but it's all without any luck... nothing seems to work properly .
What's the problem?
The response SOAP message has a body what spring calls a Payload and my SOAP client (SOAPUI) receives the response rather well.
But here it comes: how should I add new (custom) SOAP headers to the response message?
What's the response xml expected?
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<aud:HeaderInfo xmlns:bd="http://www.myws.com/">
<bd:ID>123</bd:ID>
<bd:Type>text</bd:Type>
</aud:HeaderInfo>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ne:myWS xmlns:ne="http://www.iways.com/">
<ne:INFO>
<ne:NAME>JOHN</ne:NAME>
<ne:DESIGNATION>ITA</ne:DESIGNATION>
<ne:MOBILE>9841011113</ne:MOBILE>
</ne:INFO>
</ne:myWS>
My payload
#PayloadRoot(localPart = "myWSRequest", namespace = TARGET_NAMESPACE)
public #ResponsePayload myWSResponse getInfo(#RequestPayload myWSRequest request)
{
myWSResponse response = new myWSResponse();
Person person = personService_i.getAccountDetails(request.getID());
response.setPersonDetails(person);
return response;
}
Any side info?
i use xsd which generates a load of classes based upon the XSDs I don't know how to add those custom headers to the response message,
You could implement a endpointInterceptorAdapter and do the following:
public final class MyEndpointInterceptorAdapter extends EndpointInterceptorAdapter {
#Override
public boolean handleResponse(MessageContext messageContext_, Object endpoint_)
throws IOException {
WebServiceMessage _webServiceMessage = messageContext_.getResponse();
SoapMessage _soapMessage = (SoapMessage) _webServiceMessage;
if (_soapMessage != null) {
SoapEnvelope _soapEnvelope = _soapMessage.getEnvelope();
// create your qname object
QName _myQName = ....
// adding your quname to the header
_soapEnvelope.getHeader().addHeaderElement(myQName );
}
}
}
and in your spring configuration file, just add the interceptor:
<sws:interceptors>
<ref bean="myEndpointInterceptorAdapter"/>
</sws:interceptors>
After reading doc on the Spring web site, still confused about how to extract information from a SOAP request.
For example, the SOAP request sent to server is like:
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:user="http://www.mysite.com/user/schemas">
<soapenv:Header/>
<soapenv:Body>
<user:UserRequest>
<!--You may enter the following 4 items in any order-->
<user:Key>key</user:Key>
<user:UserName>username</user:UserName>
<user:RequesterName>reqname</user:RequesterName>
<user:RequesterPassword>repw</user:RequesterPassword>
</user:UserRequest>
</soapenv:Body>
</soapenv:Envelope>
On my server side I create an Endpoint like:
#Endpoint
public class UserEndpoint {
private static final String NAMESPACE_URI = "http://www.mysite.com/user/schemas";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "UserRequest")
public void handleGetUserRequest() {
//Extract here...
}
}
How should I write extraction code here?
I would suggest having a look at the Spring WS samples for code ideas, depending on what else you are using in your application. For example: the HolidayEndpoint source code.
#Endpoint("myEndpoint")
public class MyEndpoint {
/**
* Spring-WS Endpoint
* #param submitSomethingRequest
* #param header
* #return SubmitSomethingResponse
*/
#PayloadRoot(namespace="http://my.namespace.org/spec/1.0.1", localPart="submitSomethingRequest")
#ResponsePayload
public SubmitSomethingResponse submitSomethingRequest(#RequestPayload SubmitSomethingRequest submitSomethingRequest, **SoapHeader header**) {
LOG.info("Received SOAP HEADER: " + header);
if(header != null) {
Iterator<SoapHeaderElement> hdrs = header.examineAllHeaderElements();
while(hdrs.hasNext()) {
SoapHeaderElement hdrEle = hdrs.next();
System.out.prinltn(hdrEle.getName().getPrefix() + ":" + hdrEle.getName().getLocalPart());
... //Do something here to parse DOM and extract headers you care about
}
}
...
I'm implementing a web service client using JAX-WS over SOAP. Its error codes are returned in the following way:
<?xml version = '1.0' encoding = 'UTF-8'?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<env:Header>
<!-- header stuff goes here -->
</env:Header>
<env:Body>
<env:Fault>
<abc:fault xmlns:abc="http://example.com/abc">
<abc:faultcode>12345</abc:faultcode>
<abc:faultstring>Error message goes here</abc:faultstring>
</abc:fault>
</env:Fault>
</env:Body>
</env:Envelope>
As far as I know, this is not the correct way to do SOAP faults. The subelements of a env:Fault should be <faultcode> and <faultstring>, not a different namespaced <fault>. Unfortunately, I have no way of making the web service change this.
My hope was that I would be able to parse this message in a SOAPHandler and transform it into a regular fault before passing it on to the rest of my code, however when I logged the message in an earlier Handler I saw that the Fault element completely empty. The <abc:fault> was gone!
I'm using JAX-WS on WebSphere 7 and I've tried setting "jaxws.payload.highFidelity" to true in my system properties. Any clues on to how to get at the original message?
Leaving this alone will cause a WebServiceException with a NullPointerException because JAX-WS can't find the faultcode.
So I found the answer to my question. WebSphere 7 uses Axis2. Axis2's MessageContext provides a property called "TRANSPORT_IN" which contains a ByteArrayInputStream. TRANSPORT_IN, as the name implies, contains the exact SOAP message received.
I parsed through the original SOAP message in my Handler#handleFault method using a SAXHandler to retrieve the abc:fault message. I then wrote the abc:fault > faultcode and faultstring to the soapenv:Fault faultcode and faultstring. My application then handles the SOAPFaultException as if it was a normal one.
I'm still very open to any better answers since this feels like roundabout way to do this.
Handler Code:
public boolean handleFault(SOAPMessageContext context) {
SOAPMessage m = context.getMessage();
if(m != null) {
SOAPBody body = m.getSOAPBody();
SOAPFault fault = body.getFault();
setAbcFault(fault, context);
}
}
private void setAbcFault(SOAPFault fault, MessageContext context) {
ByteArrayInputStream bis = (ByteArrayInputStream)context.get("TRANSPORT_IN");
// do sax parsing on the input stream
fault.setFaultCode(abcFaultCodeQName);
fault.setFaultString(abcFaultString);
}
If you are using JAX-WS, you can use SOAP faults. or that, you need an Exception with #WebFault annotation. You can find a good example in Using SOAP Faults and Exceptions in Java JAX-WS Web Services - Eben Hewitt on Java.
See the answer for returning null or throw exception and How to throw a custom fault on a JAX-WS web service?
Example:
#WebService
public class CalculatorWS {
public String factorial(int n) throws FactorialException {
if (n < 0) {
throw new FactorialException("Negative number!", // faultstring
"The number n = " + n); // detail
}
return BigIntegerMath.factorial(n).toString();
}
}
With:
public class FactorialException extends Exception {
String detail;
public FactorialException(String message, String detail) {
super(message);
this.detail = detail;
}
public String getFaultInfo() {
return detail;
}
}
If the request is:
<soapenv:Envelope ... >
<soapenv:Header/>
<soapenv:Body>
<test:factorial>
<arg0>-1</arg0>
</test:factorial>
</soapenv:Body>
</soapenv:Envelope>
The response is:
<soapenv:Envelope ... >
<soapenv:Body>
<soapenv:Fault>
<faultcode>soapenv:Server</faultcode>
<faultstring>Negative number!</faultstring>
<detail>
<ns2:FactorialExceptionBean xmlns:ns2="http://...">
The number n = -1
</ns2:FactorialExceptionBean>
</detail>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
(Tested in Websphere 7)
Here is my SOAP request:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:str="http://app.strategyblocks.com/ws/schema/strategyblocks">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="...">
<wsse:Username>admin</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">secret</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<str:updateKpiRequest>
<str:company_id>1</str:company_id>
<str:kpi>
<str:external_id>1134511</str:external_id>
<str:title>title</str:title>
<str:description>description</str:description>
</str:kpi>
</str:updateKpiRequest>
</soapenv:Body>
</soapenv:Envelope>
Here is my Endpoint class:
#Endpoint
public class UpdateKpiEndpoint {
// The namespace of both request and response as declared in the XSD file
public static final String NAMESPACE_URI = "http://app.strategyblocks.com/ws/schema/strategyblocks";
// The local name of the expected request.
public static final String REQUEST_LOCAL_NAME = "updateKpiRequest";
#PayloadRoot(localPart = REQUEST_LOCAL_NAME, namespace = NAMESPACE_URI)
#ResponsePayload
public UpdateKpiResponse processUpdateKpi(#RequestPayload UpdateKpiRequest updateKpiRequest) {
try {
} catch (Exception e) {
UpdateKpiResponse response = new UpdateKpiResponse();
response.setCode("FAILURE");
response.setDescription("Problem with update kpi request");
return response;
}
UpdateKpiResponse response = new UpdateKpiResponse();
response.setCode("SUCCESS");
response.setDescription("Kpi has been updated");
return response;
}
}
At the moment I am passing a UsernameToken for authentication in the soap request, that is all working well and I have no problems with it what so ever. What I want to be able to achieve is to retrieve that username from the header in the body of processUpdateKpi method in my endpoint class, so that I can use it to find existing data for that user, I have tried to find examples of it being done and so far I have been unsuccessful, is it possible to do it? I have thought about also passing the username in the SOAP body as well, but I want to avoid it.
someone in the spring forums had a clear explanation on how to read the header from the endpoint class:
http://forum.springsource.org/showthread.php?109560-Unable-to-read-SoapHeader-in-Endpoint-class