I've been tasked with having to send a SOAP request using Okhttp with a String marshaled by JAXB using classes generated by Wsimport. I've marshaled the body of the request however, I need to further wrap it in a Envelope and Body tag. I was encouraged not to build SOAP bodies with QName and namespaces in my code but to rather make use of the generated classes.
This is my code using JAXB to marshal the request body from the Wsimport class:
JAXBContext jaxbContext = JAXBContext.newInstance(CelsiusToFahrenheit.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(request, sw);
String xmlBody = sw.toString();
System.out.println(xmlBody);
Right now all I have is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CelsiusToFahrenheit xmlns="https://www.w3schools.com/xml/">
<Celsius>25</Celsius>
</CelsiusToFahrenheit>
What I need is:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<CelsiusToFahrenheit xmlns="https://www.w3schools.com/xml/">
<Celsius>string</Celsius>
</CelsiusToFahrenheit>
</soap:Body>
</soap:Envelope>
For extra context, the api I'm using is:
https://www.w3schools.com/xml/tempconvert.asmx
Related
I'm building a service that sends a POST request to UPS track package API. In PostMan, this call returns a 200 with this URL and these POST params
URL: https://onlinetools.ups.com/ups.app/xml/Track
Body: form-data
Params:
UPSSecurity
<?xml version="1.0"?> <AccessRequest xml:lang="en-US"> <AccessLicenseNumber>REDACTED</AccessLicenseNumber> <UserId>REDACTED</UserId> <Password>REDACTED</Password> </AccessRequest>
Body
<?xml version="1.0"?> <TrackRequest xml:lang="en-US"> <Request> <TransactionReference> <CustomerContext>Your Test Case Summary Description</CustomerContext> <XpciVersion>1.0</XpciVersion> </TransactionReference> <RequestAction>Track</RequestAction> <RequestOption>activity</RequestOption> </Request> <TrackingNumber>REDACTED</TrackingNumber> </TrackRequest>
Our application defines clients using OpenFeign and Spring annotations. The solution I'm going to try is to define my FeignClient like this:
interface UPSClient {
#Headers({"SOAPAction: http://onlinetools.ups.com/webservices/TrackBinding/v2.0","Content-Type: application/xml"})
#PostMapping(
value = "/Track",
consumes = MediaType.TEXT_XML_VALUE,
produces = MediaType.TEXT_XML_VALUE
)
ResponseEntity<TrackResponse> getTrackResponse(#RequestBody UPSRequest upsRequest);
and define MyRequestObject as
public class UPSRequest {
String UPSSecurity; // UPSSecurity is a POJO generated from UPS wsdl and marshalled into an XML String
String Body; // Body is a POJO generated from UPS wsdl and marshalled into an XML String
}
Is there a cleaner approach that anyone could recommend? The TrackResponse returned from the UPS endpoint can be handled by a custom SOAPDecoder and shouldn't be an issue
I have a Soap service generated by a wsdl file, that expects a certain TargetNamespace
#WebResult(name = "getResponse", targetNamespace = "http://targetNameSpace1.com", partName = "result")
but we have multiple clients calling this api and each one uses a diferente TargetNamespace:
Client one:
<soap:Envelope
xmlns:loc="http://targetNameSpace1.com">
<soap:Header>
<ns3:RequestSOAPHeader>
...
</ns3:RequestSOAPHeader>
</soap:Header>
<soap:Body>
<loc:getResponse>
<loc:value>url/</loc:value>
</loc:getResponse>
</soap:Body>
</soap:Envelope>
Client two:
<soap:Envelope
xmlns:loc="http://targetNameSpace2.com">
<soap:Header>
<ns3:RequestSOAPHeader>
...
</ns3:RequestSOAPHeader>
</soap:Header>
<soap:Body>
<loc:getResponse>
<loc:value>url/</loc:value>
</loc:getResponse>
</soap:Body>
</soap:Envelope>
This is the error i get:
<soap:Envelope>
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>Message part {http://targetNameSpace2.com}getResponse was not recognized. (Does it exist in service WSDL?)</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
and i can only make it work with one at a time changing the targetNameSpace tag in the webResult, but my ultimate goal is to ignore this tag, because i dont know witch namespace will each client use.
At the moment i am trying to use an interceptor that extends this
AbstractSoapInterceptor and i get a SoapMessage object i can acess it before the request is made, but i can't seem to change the request, not sure if its the best aproach.
Does anyone have a solution for this?
Thanks!
The wsdl and the (embedded) xsd are the contracts that you specify. Server and client need to follow that contract. Changing the contract single sided will result into invalid messages. Instead of looking for a solution to accept invalid messages you should update the clients so they obey the contracts.
I ended up following this article https://www.javatips.net/blog/cxf-interceptor-example,
I intercept every request, and replace this:
xmlns:loc="http://targetNameSpace2.com"
with the url that i want, i used regex to replace whats inside the loc tag
I have a system that returns SOAP envelope message
<soap:Envelope>
<soap:Body>
<ns2:myResponse>
And i have classes generated from WADL and XSD files (by cxf-wadl2java-plugin and JAXB), also i have an interface generated (imports from javax.ws.rs ):
#Path("/case/{id}")
public interface MyService {
#GET
#Produces("application/xml")
Response getCase(#PathParam("id") String id);
How should the client looks like ?
I've tried with Jersey:
com.sun.jersey.api.client.Client client = Client.create();
WebResource resource = client.resource("http://localhost:8080/case");
and CXF:
MyService service = JAXRSClientFactory.create("http://localhost:8080", MyService.class, providers);
but during a call i have (i'm not surprised) a problem with unmarshalling:
unexpected element (uri:"http://www.w3.org/2003/05/soap-envelope", local:"Envelope"). Expected elements are <{}myResponse>
how to create working client for this?
it was a WS before (with WSDL, binding file and XSDs with cxf-codegen-plugin plugin)
A web service I'm connecting to throws a SOAPFaultException for all its exceptions which means the app throws only RuntimeExceptions.
Using wsimport I generated the code and objects to connect and communicate with the service, it also generated the JAXB objects which represents the xml content in the detail element of the SOAPFaultException.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Soap Exception From Server</faultstring>
<faultactor>url</faultactor>
<detail>
<MyResponse>
<myID>123</myId>
<serverName>Test</serverName>
<error>
<desc>Unable to verify id</desc>
</error>
</MyResponse>
</detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>
Wsimport generated a jaxb object called MyResponse with myId,serverName, error,desc as variables
I want to know how can I populate the MyResponse jaxb object that was generated, with the detail element data or do I have to navigate through the elements by node to get the values?
So I have a web service with several namespaces that I would like to route through a bean to do some checking of user credentials. Its been a long time since I used XPATH so I might just be having a PICNIC(Problem In Chair Not In Computer Moment) error.
The web service message will always have the following structure/pattern :
<Operation>
<header with the head name space where the user credentials are stored>
<record control>
<objXX>
</Operation>
Here is a example message(SOAP UI):
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:list="http://www.insol.irix.com.au/IRIX_V1/Debtors/List" xmlns:head="http://www.insol.irix.com.au/IRIX_V1/Headers" xmlns:rec="http://www.insol.irix.com.au/IRIX_V1/RecordControl">
<soapenv:Header/>
<soapenv:Body>
<list:ListDebtorReq>
<head:MsgReqHdr>
<head:MsgGUID>${=java.util.UUID.randomUUID()}</head:MsgGUID>
<head:MsgDateTime>${=javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar(GregorianCalendar.getInstance())}</head:MsgDateTime>
<head:ConsumerSystemIDInfo>
<head:ConsumerSystemID>ConsumerSystemID</head:ConsumerSystemID>
<head:ConsumerSystemUserID>AgentX</head:ConsumerSystemUserID>
</head:ConsumerSystemIDInfo>
<head:SecCredInfo>
<head:IRIXUserID>Some User ID</head:IRIXUserID>
<head:IRIXPassword>Some Password</head:IRIXPassword>
</head:SecCredInfo>
<head:CryptoInfo>
<head:DigitalSignatureInfo>
<head:DigitalSignatureValue>verrantque per auras</head:DigitalSignatureValue>
<head:DigitalSignatureAlgorithm>SHA-256</head:DigitalSignatureAlgorithm>
</head:DigitalSignatureInfo>
</head:CryptoInfo>
</head:MsgReqHdr>
<!--Optional:-->
<rec:RecCntrl>
<rec:StartRecordNumber>1</rec:StartRecordNumber>
<!--Optional:-->
<rec:NumberOfRecords>3</rec:NumberOfRecords>
</rec:RecCntrl>
</list:ListDebtorReq>
</soapenv:Body>
</soapenv:Envelope>
So essentially I want to be able to create a bean that will be able to query the MsgReq header for all the user name and password data. To simplify things I am just trying to query the MsgGUID and work my way from there. However I cant seem to get the xpath right. Since I am using several namespaces I have included them in the camel context file just to make sure they are available.
Here is my camel-context:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:camel="http://camel.apache.org/schema/spring"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd">
<import resource="classpath:META-INF/spring/camel-cxf.xml" />
<bean id="SecurityCheckBean" class="au.com.irix.insol.Security.IRIXSecurity"/>
<camelContext xmlns="http://camel.apache.org/schema/spring"
xmlns:list="http://www.insol.irix.com.au/IRIX_V1/Debtors/List"
xmlns:head="http://www.insol.irix.com.au/IRIX_V1/Headers"
xmlns:rec="http://www.insol.irix.com.au/IRIX_V1/RecordControl">
<route>
<from uri="cxf:bean:DebtorsService?dataFormat=PAYLOAD"/>
<bean ref="SecurityCheckBean"/>
</route>
</camelContext>
</beans>
As you can see I am running the incoming message of the web service producer to the SecurityCheckBean. My SecurityCheckBean is super simple at the moment see code below.
public class IRIXSecurity {
public void CheckCredentials(
#XPath("//head:MsgGUID") String msgGUID,
#Body String body){
System.out.println(body);
System.out.println("Check Credentials Invoked");
System.out.println(msgGUID);
}
}
However when I send a send a request via soap UI I get the following exception:
Invalid xpath: //head:MsgGUID. Reason: javax.xml.xpath.XPathExpressionException: net.sf.saxon.trans.XPathException: Prefix head has not been declared
So how do I go about retrieving this information? Why even though I have declared the name spaces in my camel-context.xml they are reported as missing?
Just for interest sake I have tried several variations of the XPATH such as:
#XPath("//MsgGUID")
#XPath("//MsgReqHdr/head:MsgGUID")
#XPath("//head:MsgReqHdr/head:MsgGUID")
Every time I either get an exception as listed above or a NULL value...
Right got it to work. When dealing with the namespaces in a bean the following syntax must be used to include the namespaces.
public class IRIXSecurity {
public void CheckCredentials(
//#Body ListDebtorReqType msgBody, #Headers Map hdr,
#XPath(value="//header:MsgGUID",namespaces = #NamespacePrefix(
prefix = "header",
uri = "http://www.insol.irix.com.au/IRIX_V1/Headers")) String msgGUID,
#Body Document xml)
{
System.out.println("Check Credentials Invoked");
System.out.println(msgGUID);
//exchange.getOut().setBody(debtorRsType);
}
}