Generating SOAP Requests from Java objects - soap

I need to create a Soap request from a request object in Java. What is needed is below :
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<tem:tag1>
<tem:tag2>
<MyDataSet>
<!-- more elements within-->
</MyDataSet>
<tem:tag1>
<tem:tag2>
</SOAP-ENV:Body>
<atom/>
</SOAP-ENV:Envelope>
However, what I am getting is this :
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<MyDataSet>
<!-- more elements within-->
</MyDataSet>
</SOAP-ENV:Body>
<atom/>
</SOAP-ENV:Envelope>
Can someone please tell me how do I add <tem:tag1> and <tem:tag2> in the soap request ? This is the code that I have written so far:
public static void main(String[] args) throws Exception
{
MyRequest request = new MyRequest();
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Marshaller marshaller = JAXBContext.newInstance(MyRequest.class).createMarshaller();
marshaller.marshal(request, document);
SOAPMessage soapMessage = MessageFactory.newInstance().createMessage();
soapMessage.getSOAPPart().getEnvelope().addNamespaceDeclaration("tem", "http://tempuri.org/");
soapMessage.getSOAPBody().addDocument(document);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
soapMessage.writeTo(outputStream);
String output = new String(outputStream.toByteArray());
System.out.println(output);
}

Assuming that the web service has published WSDL, I suggest that instead of rolling your own client code, you generate it using wsimport, a utility that is included in the Java JDK. One article on how to use it can be found here.

Related

How to handle soap:mustUnderstand header in Spring WS

An external system is sending my service a SOAP message and I have a listener in place
#Endpoint
public class NotificationListener {
private static final String NAMESPACE_URI = "http://test.com/test";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "Notification")
#ResponsePayload
public void getSOAPMessage(#RequestPayload HistoryMessage request) {
// calls to methods in other classes which handle the business logic
}
The request body contains the following headers:
<soap:Header>
<wsa:To soap:mustUnderstand="1"
xmlns:wsa="http://www.w3.org/2005/08/addressing">{destination endpoint}
</wsa:To>
<wsa:From
xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
</wsa:From>
<wsa:ReplyTo
xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:FaultTo
xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
</wsa:FaultTo>
<wsa:Action soap:mustUnderstand="1"
xmlns:wsa="http://www.w3.org/2005/08/addressing">
</wsa:Action>
<wsa:MessageID
xmlns:wsa="http://www.w3.org/2005/08/addressing">urn:uuid:fa163e6e-ef55-1eec-b9ac-5e80af1d126a
</wsa:MessageID>
I get the following error on calling my endpoint:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:MustUnderstand</faultcode>
<faultstring xml:lang="en">One or more mandatory SOAP header blocks not understood</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I understand that the mustUnderstand attribute in the header is set to 1 which means true and that header must be handled. But how do I handle it?
I'm using Spring WS to build the listener service.
TIA:)

migrate axis soap webservice to spring ws - RequestPayload object values coming as null in endpoint method

Working with an old wsdl file generated with AXIS to make it work with spring ws. After doing some tweaks and all, i could generate the java sources with the old wsdl.
Now i am trying to make a request from soap UI , But request values are shown as null in endpoint method. Request is coming in backend properly but not values.
WSDL file
<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:onl="http://online.mysite.com">
<soapenv:Header/>
<soapenv:Body>
<onl:getSummary soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<in0 xsi:type="onl:SummaryObject">
<docid xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">doc123</docid>
<amount xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">?</amount>
<duenew xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">?</duenew>
<reference xsi:type="xsd:long">?</reference>
<sortBy xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">?</sortBy>
<startDate xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">?</startDate>
</in0>
</onl:getSummary>
</soapenv:Body>
</soapenv:Envelope>
Soap Request:
<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:onl="http://online.mysite.com">
<soapenv:Header/>
<soapenv:Body>
<onl:getSummary soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<in0 xsi:type="onl:SummaryObject">
<docid xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">doc123</docid>
<amount xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">?</amount>
<duenew xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">?</duenew>
<reference xsi:type="xsd:long">121212121</reference>
<sortBy xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">?</sortBy>
<startDate xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">?</startDate>
<visibility xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">?</visibility>
</in0>
</onl:getSummary>
</soapenv:Body>
</soapenv:Envelope>
Endpoint Method:
#PayloadRoot(namespace = NAMESPACE_URI, localPart ="getSummary")
#ResponsePayload
public JAXBElement<EObjects> getSummary(#RequestPayload SummaryObject summaryObject) {
System.out.println("Am done with this"+summaryObject.getDocId());
ObjectFactory factory = new ObjectFactory();
EObjects objects = factory.createEObjects();
QName qname = new QName("http://online.mysite.com", "eobjects");
return new JAXBElement(qname, EObjects.class, objects);
}
WSDL generated in axis 1 is no longer been supported by spring ws or CXF. So that generated java classes from WSDL wont have required information that is needed for unmarshelling of request by JAXB in spring. Thus request object will come as null.
I have done a work around which has 2 things to do
Add xml root element annotation on top of the request object class generated from WSDL.
#XmlRootElement(name="getSomething",namespace =
"http://yoursite.com")
unmarshell the request object manually as shown below:
code
SoapMessage message = (SoapMessage) messageContext.getRequest();
ByteArrayOutputStream out = new ByteArrayOutputStream();
message.writeTo(out);
String strMsg = new String(out.toByteArray()); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document d = db.parse(new InputSource(new StringReader(strMsg)));
Node getrequestObject = d.getElementsByTagName("yourtag").item(0);
JAXBContext jc = JAXBContext.newInstance(MyRequestObject.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement<SummaryObject> je = unmarshaller.unmarshal(new
DOMSource(getrequestObject), MyRequestObject.class);

Customizing Spring Integration Web Service SOAP Envelope/Header

I am trying to send a SOAP request using Spring Integration like
<int:chain input-channel="wsOutChannel" output-channel="stdoutChannel">
<int-ws:header-enricher>
<int-ws:soap-action value="..."/>
</int-ws:header-enricher>
<int-ws:outbound-gateway
uri="..."/>
</int:chain>
but you can only add the SOAP body, and Spring Integration adds the envelope, header, and body tags like
<SOAP-ENV:Envelope>
<SOAP-ENV:Header>
<SOAP-ENV:Body>
...
</SOAP-ENV:Body>
<SOAP-ENV:Header>
</SOAP-ENV:Envelope>
I need to customize the envelope and header tags with specific attributes, for example:
<soapenv:Envelope attribute1="value1" attribute2="value2">
and child elements, for example:
<soapenv:Header>
<child>...<child>
<soapenv:Header>
Is this possible with Spring Integration Web Services, or should I not use int-ws:outbound-gateway and take a different approach?
You can add a ClientInterceptor (via the interceptor attribute) which allows you to modify the request before it's sent out.
EDIT
#Artem's suggestion is simpler but the interceptor gives you access to the response too; but either way, the code is similar.
For the interceptor:
public class MyInterceptor extends ClientInterceptorAdapter {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
SoapMessage request = (SoapMessage) messageContext.getRequest();
SoapEnvelope envelope = request.getEnvelope();
envelope.addAttribute(new QName("foo"), "bar");
SoapHeader header = envelope.getHeader();
header.addHeaderElement(new QName("http://fiz/buz", "baz"));
return super.handleRequest(messageContext);
}
}
For the callback version:
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
SoapEnvelope envelope = ((SoapMessage) message).getEnvelope();
envelope.addAttribute(new QName("foo"), "bar");
SoapHeader header = envelope.getHeader();
header.addHeaderElement(new QName("http://fiz/buz", "baz"));
}
I thing you can inject WebServiceMessageCallback:
<xsd:attribute name="request-callback" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Reference to a Spring Web Services WebServiceMessageCallback. This enables changing
the Web Service request message after the payload has been written to it but prior
to invocation of the actual Web Service.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.ws.client.core.WebServiceMessageCallback"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
and cast the message to the SoapMessage and use its getEnvelope() to customize a desired way.

Spring Integration should route Message (with SOAP Headers)

I am trying to write configure a gateway, which should take a complete SOAP Message and then delegate it to another SOAP Provider (incl. all SOAP headers of the first request).
What I have done so far:
1) web.xml
MessageDispatcherServlet with Mapping:
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/appservices/*</url-pattern>
</servlet-mapping>
2) Configuration with an Endpoint-Mapping
<bean class="org.springframework.ws.server.endpoint.mapping.UriEndpointMapping">
<property name="defaultEndpoint" ref="ws-in-gw"/>
</bean>
3) Configuration of Spring Integration inbound-gateway and outbound-gateway
<int-ws:inbound-gateway id="ws-in-gw"
request-channel="in"
reply-channel="out"
mapped-request-headers="*" />
<int:channel id="in" />
<int:channel id="out" />
<int-ws:outbound-gateway
id="ws-out-gw-status"
request-channel="in-status"
reply-channel="out-status"
uri="http://${delegationServer}/${delegation.contextroot}/soap/AnotherService"
interceptor="soapEnricher"
</int-ws:outbound-gateway>
<bean id="soapEnricher" class="foo.bar.SoapHeaderEnricher" />
public class SoapHeaderEnricher implements ClientInterceptor {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
try {
SoapMessage soapMessage = (SoapMessage) messageContext.getRequest();
SoapHeader sh = soapMessage.getSoapHeader();
// can use sh.addHeaderElement(new QName(...)) now, but where are the original Headers???
} catch () {
}
}
My first Problem was, that the original SOAP Headers had been cut of, so I introduced the ' mapped-request-headers="*" ' attribute at the inbound gateway.
When I now configure a wire-tap, I see the Headers (myToken:MySecretToken) are received:
DEBUG 10:46:53 - [Payload DOMSource content=javax.xml.transform.dom.DOMSource#24a6ce98][Headers={errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#43456ff4, myToken:MySecretToken=org.springframework.ws.soap.saaj.SaajSoapHeaderElement#3b91ead, ...}]
This is the SOAP Message for my test:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:stat="http://status.service.promakler.provinzial.com/">
<soapenv:Header>
<myToken:MySecretToken xmlns=""
xmlns:myToken="http://foo.bar">12345</myToken:MySecretToken>
</soapenv:Header>
<soapenv:Body>
<stat:getStatus/>
</soapenv:Body>
</soapenv:Envelope>
So the Headers are now in my Message, but in the ClientInterceptor, there is no way to get the Headers (just the payload)?! I can add new Headers, but how can I get the original Header?
Can anybody give me a hint (or perhaps there is even a quiet simpler solution??)
Regards
Timo
Try to introduce a custom extension of DefaultSoapHeaderMapper and override populateUserDefinedHeader to extract those SaajSoapHeaderElement from the MessageHeaders and populate them to the SoapHeader. And finally inject your solution to the header-mapper of your <int-ws:outbound-gateway>.

SOAP Web Service Client in Borland C++ Builder 6

Here below is the step-by-step attempt to developp a Web Service client in an application developped in Borland C++ Builder 6.
Help is welcome to correct this code or to suggest any other solution (though I now try to use a dll developped in C++ Builder XE3).
Here is my attempt to use Soap in Borland 6 C++ Builder. Untill now I conclude that I cannot succeed because Borland 6 does not manage SOAP header (absent of InvokeRegistry.hpp) and the SOAP server to whom we send requests needs cookies to be passed after the login of this interface :
__interface INTERFACE_UUID("{B0F412ED-AC6A-42C3-8730-DD0D9680F16D}") AuthenticationSoap : public IInvokable
{
public:
virtual LoginResult* Login(const AnsiString username, const AnsiString password) = 0;
virtual AuthenticationMode Mode() = 0;
};
typedef DelphiInterface<AuthenticationSoap> _di_AuthenticationSoap;
this login first sends a :
500 Server Internal Error
then is ok if I add this call :
InvRegistry()->RegisterInvokeOptions(__interfaceTypeinfo(AuthenticationSoap), ioDocument);
then I pass the User Name to a HTTPRIO :
MyHTTPRIO->HTTPWebNode->UserName = AnsiString("xxx...");
And this HTTPRIO is passed to the second interface :
__interface INTERFACE_UUID("{1E5B3820-A40E-FD40-326D-95A9F6B7A5F0}") OrganizerWS_1_1Soap : public IInvokable
{
public:
...
virtual void findHorse(const ArrayOfString FEIIDs, const AnsiString Name, const AnsiString SexCode, const bool IsPony, const AnsiString AthleteFEIID, ArrayOfHorseOC& findHorseResult, ArrayOfMessage& Messages) = 0;
...
};
typedef DelphiInterface<OrganizerWS_1_1Soap> _di_OrganizerWS_1_1Soap;
_di_OrganizerWS_1_1Soap GetOrganizerWS_1_1Soap(bool useWSDL=false, AnsiString addr="", Soaphttpclient::THTTPRIO* HTTPRIO=0);
But we get an error "AuthHeader is missing" because we have used Borland 6's WSDL importer which does not generates code for Headers and headers are not defined in Invokeregistry.hpp.
So in the "BeforeExecute" of our HTTPRIO we have this request :
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<findHorse xmlns="http://fei.org/">
<FEIIDs/>
<Name></Name>
<SexCode></SexCode>
<IsPony>false</IsPony>
<AthleteFEIID>10002254</AthleteFEIID>
<findHorseResult/>
<Messages/>
</findHorse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
whereas Borland XE3 generates this request which is ok and brings the expected response from the server :
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Header>
<NS1:AuthHeader xmlns:NS1="http://fei.org/">
<UserName xmlns="http://fei.org/">xxx...</UserName>
<Language xmlns="http://fei.org/">en</Language>
</NS1:AuthHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<findHorse xmlns="http://fei.org/">
<IsPony>false</IsPony>
<AthleteFEIID>10002254</AthleteFEIID>
</findHorse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
So I first replaced
<SOAP-ENV:Body>
by
<SOAP-ENV:Header><NS1:AuthHeader xmlns:NS1="http://fei.org/"><UserName xmlns="http://fei.org/">xxx...</UserName><Language xmlns="http://fei.org/">en</Language></NS1:AuthHeader></SOAP-ENV:Header><SOAP-ENV:Body>
then added options as for example "<< xoHolderClass << xoInlineArrays" :
RemClassRegistry()->RegisterSerializeOptions(__classid(findHorse), (TSerializationOptions() << xoHolderClass << xoInlineArrays));
here I obtain the same request but the answer of the server is a NULL array whereas there are three items in the answer to a test written in borland xe3.
Or with other manipulations (without xo... options) I get access violation in delphi interfaces destructor :
__fastcall ~DelphiInterface<T>()
{
if (intf != 0)
{
intf->Release();
intf = 0;
}
}
I explored other solutions (such as using indySOAP which is available only in Delphi sources (no C++ sources), thought about creating the XML and sending with Indy components (but expect I would encounter a same authentication header issue ?), using CAPICOM, or windows SDK ? ...) but without success until now and as said before I now try to use a dll developped in C++ Builder XE3 :
https://stackoverflow.com/questions/15485276/embarcadero-c-xe3-dll-imported-in-c-xe3-project-library-loads-but-access-v